|
@@ -28,8 +28,13 @@ import subprocess
|
|
from functools import partial
|
|
from functools import partial
|
|
from collections import defaultdict
|
|
from collections import defaultdict
|
|
|
|
|
|
-from PyQt4.QtGui import *
|
|
|
|
-from PyQt4.QtCore import *
|
|
|
|
|
|
+try:
|
|
|
|
+ from PyQt5.QtGui import *
|
|
|
|
+ from PyQt5.QtCore import *
|
|
|
|
+ from PyQt5.QtWidgets import *
|
|
|
|
+except ImportError:
|
|
|
|
+ from PyQt4.QtGui import *
|
|
|
|
+ from PyQt4.QtCore import *
|
|
|
|
|
|
from labelme import resources
|
|
from labelme import resources
|
|
from labelme.lib import struct, newAction, newIcon, addActions, fmtShortcut
|
|
from labelme.lib import struct, newAction, newIcon, addActions, fmtShortcut
|
|
@@ -71,7 +76,7 @@ class WindowMixin(object):
|
|
|
|
|
|
def toolbar(self, title, actions=None):
|
|
def toolbar(self, title, actions=None):
|
|
toolbar = ToolBar(title)
|
|
toolbar = ToolBar(title)
|
|
- toolbar.setObjectName(u'%sToolBar' % title)
|
|
|
|
|
|
+ toolbar.setObjectName('%sToolBar' % title)
|
|
#toolbar.setOrientation(Qt.Vertical)
|
|
#toolbar.setOrientation(Qt.Vertical)
|
|
toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
|
toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
|
if actions:
|
|
if actions:
|
|
@@ -81,7 +86,7 @@ class WindowMixin(object):
|
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow, WindowMixin):
|
|
class MainWindow(QMainWindow, WindowMixin):
|
|
- FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = range(3)
|
|
|
|
|
|
+ FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = 0, 1, 2
|
|
|
|
|
|
def __init__(self, filename=None, output=None):
|
|
def __init__(self, filename=None, output=None):
|
|
super(MainWindow, self).__init__()
|
|
super(MainWindow, self).__init__()
|
|
@@ -99,8 +104,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.labelDialog = LabelDialog(parent=self)
|
|
self.labelDialog = LabelDialog(parent=self)
|
|
|
|
|
|
self.labelList = QListWidget()
|
|
self.labelList = QListWidget()
|
|
- self.itemsToShapes = {}
|
|
|
|
- self.shapesToItems = {}
|
|
|
|
|
|
+ self.itemsToShapes = []
|
|
|
|
|
|
self.labelList.itemActivated.connect(self.labelSelectionChanged)
|
|
self.labelList.itemActivated.connect(self.labelSelectionChanged)
|
|
self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged)
|
|
self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged)
|
|
@@ -119,8 +123,8 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
listLayout.addWidget(self.labelList)
|
|
listLayout.addWidget(self.labelList)
|
|
|
|
|
|
|
|
|
|
- self.dock = QDockWidget(u'Polygon Labels', self)
|
|
|
|
- self.dock.setObjectName(u'Labels')
|
|
|
|
|
|
+ self.dock = QDockWidget('Polygon Labels', self)
|
|
|
|
+ self.dock.setObjectName('Labels')
|
|
self.dock.setWidget(self.labelListContainer)
|
|
self.dock.setWidget(self.labelListContainer)
|
|
|
|
|
|
self.zoomWidget = ZoomWidget()
|
|
self.zoomWidget = ZoomWidget()
|
|
@@ -152,67 +156,67 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
# Actions
|
|
# Actions
|
|
action = partial(newAction, self)
|
|
action = partial(newAction, self)
|
|
quit = action('&Quit', self.close,
|
|
quit = action('&Quit', self.close,
|
|
- 'Ctrl+Q', 'quit', u'Quit application')
|
|
|
|
|
|
+ 'Ctrl+Q', 'quit', 'Quit application')
|
|
open = action('&Open', self.openFile,
|
|
open = action('&Open', self.openFile,
|
|
- 'Ctrl+O', 'open', u'Open image or label file')
|
|
|
|
|
|
+ 'Ctrl+O', 'open', 'Open image or label file')
|
|
save = action('&Save', self.saveFile,
|
|
save = action('&Save', self.saveFile,
|
|
- 'Ctrl+S', 'save', u'Save labels to file', enabled=False)
|
|
|
|
|
|
+ 'Ctrl+S', 'save', 'Save labels to file', enabled=False)
|
|
saveAs = action('&Save As', self.saveFileAs,
|
|
saveAs = action('&Save As', self.saveFileAs,
|
|
- 'Ctrl+Shift+S', 'save-as', u'Save labels to a different file',
|
|
|
|
|
|
+ 'Ctrl+Shift+S', 'save-as', 'Save labels to a different file',
|
|
enabled=False)
|
|
enabled=False)
|
|
close = action('&Close', self.closeFile,
|
|
close = action('&Close', self.closeFile,
|
|
- 'Ctrl+W', 'close', u'Close current file')
|
|
|
|
|
|
+ 'Ctrl+W', 'close', 'Close current file')
|
|
color1 = action('Polygon &Line Color', self.chooseColor1,
|
|
color1 = action('Polygon &Line Color', self.chooseColor1,
|
|
- 'Ctrl+L', 'color_line', u'Choose polygon line color')
|
|
|
|
|
|
+ 'Ctrl+L', 'color_line', 'Choose polygon line color')
|
|
color2 = action('Polygon &Fill Color', self.chooseColor2,
|
|
color2 = action('Polygon &Fill Color', self.chooseColor2,
|
|
- 'Ctrl+Shift+L', 'color', u'Choose polygon fill color')
|
|
|
|
|
|
+ 'Ctrl+Shift+L', 'color', 'Choose polygon fill color')
|
|
|
|
|
|
createMode = action('Create\nPolygo&ns', self.setCreateMode,
|
|
createMode = action('Create\nPolygo&ns', self.setCreateMode,
|
|
- 'Ctrl+N', 'new', u'Start drawing polygons', enabled=False)
|
|
|
|
|
|
+ 'Ctrl+N', 'new', 'Start drawing polygons', enabled=False)
|
|
editMode = action('&Edit\nPolygons', self.setEditMode,
|
|
editMode = action('&Edit\nPolygons', self.setEditMode,
|
|
- 'Ctrl+J', 'edit', u'Move and edit polygons', enabled=False)
|
|
|
|
|
|
+ 'Ctrl+J', 'edit', 'Move and edit polygons', enabled=False)
|
|
|
|
|
|
create = action('Create\nPolygo&n', self.createShape,
|
|
create = action('Create\nPolygo&n', self.createShape,
|
|
- 'Ctrl+N', 'new', u'Draw a new polygon', enabled=False)
|
|
|
|
|
|
+ 'Ctrl+N', 'new', 'Draw a new polygon', enabled=False)
|
|
delete = action('Delete\nPolygon', self.deleteSelectedShape,
|
|
delete = action('Delete\nPolygon', self.deleteSelectedShape,
|
|
- 'Delete', 'delete', u'Delete', enabled=False)
|
|
|
|
|
|
+ 'Delete', 'delete', 'Delete', enabled=False)
|
|
copy = action('&Duplicate\nPolygon', self.copySelectedShape,
|
|
copy = action('&Duplicate\nPolygon', self.copySelectedShape,
|
|
- 'Ctrl+D', 'copy', u'Create a duplicate of the selected polygon',
|
|
|
|
|
|
+ 'Ctrl+D', 'copy', 'Create a duplicate of the selected polygon',
|
|
enabled=False)
|
|
enabled=False)
|
|
|
|
|
|
advancedMode = action('&Advanced Mode', self.toggleAdvancedMode,
|
|
advancedMode = action('&Advanced Mode', self.toggleAdvancedMode,
|
|
- 'Ctrl+Shift+A', 'expert', u'Switch to advanced mode',
|
|
|
|
|
|
+ 'Ctrl+Shift+A', 'expert', 'Switch to advanced mode',
|
|
checkable=True)
|
|
checkable=True)
|
|
|
|
|
|
hideAll = action('&Hide\nPolygons', partial(self.togglePolygons, False),
|
|
hideAll = action('&Hide\nPolygons', partial(self.togglePolygons, False),
|
|
- 'Ctrl+H', 'hide', u'Hide all polygons',
|
|
|
|
|
|
+ 'Ctrl+H', 'hide', 'Hide all polygons',
|
|
enabled=False)
|
|
enabled=False)
|
|
showAll = action('&Show\nPolygons', partial(self.togglePolygons, True),
|
|
showAll = action('&Show\nPolygons', partial(self.togglePolygons, True),
|
|
- 'Ctrl+A', 'hide', u'Show all polygons',
|
|
|
|
|
|
+ 'Ctrl+A', 'hide', 'Show all polygons',
|
|
enabled=False)
|
|
enabled=False)
|
|
|
|
|
|
help = action('&Tutorial', self.tutorial, 'Ctrl+T', 'help',
|
|
help = action('&Tutorial', self.tutorial, 'Ctrl+T', 'help',
|
|
- u'Show screencast of introductory tutorial')
|
|
|
|
|
|
+ 'Show screencast of introductory tutorial')
|
|
|
|
|
|
zoom = QWidgetAction(self)
|
|
zoom = QWidgetAction(self)
|
|
zoom.setDefaultWidget(self.zoomWidget)
|
|
zoom.setDefaultWidget(self.zoomWidget)
|
|
self.zoomWidget.setWhatsThis(
|
|
self.zoomWidget.setWhatsThis(
|
|
- u"Zoom in or out of the image. Also accessible with"\
|
|
|
|
|
|
+ "Zoom in or out of the image. Also accessible with"\
|
|
" %s and %s from the canvas." % (fmtShortcut("Ctrl+[-+]"),
|
|
" %s and %s from the canvas." % (fmtShortcut("Ctrl+[-+]"),
|
|
fmtShortcut("Ctrl+Wheel")))
|
|
fmtShortcut("Ctrl+Wheel")))
|
|
self.zoomWidget.setEnabled(False)
|
|
self.zoomWidget.setEnabled(False)
|
|
|
|
|
|
zoomIn = action('Zoom &In', partial(self.addZoom, 10),
|
|
zoomIn = action('Zoom &In', partial(self.addZoom, 10),
|
|
- 'Ctrl++', 'zoom-in', u'Increase zoom level', enabled=False)
|
|
|
|
|
|
+ 'Ctrl++', 'zoom-in', 'Increase zoom level', enabled=False)
|
|
zoomOut = action('&Zoom Out', partial(self.addZoom, -10),
|
|
zoomOut = action('&Zoom Out', partial(self.addZoom, -10),
|
|
- 'Ctrl+-', 'zoom-out', u'Decrease zoom level', enabled=False)
|
|
|
|
|
|
+ 'Ctrl+-', 'zoom-out', 'Decrease zoom level', enabled=False)
|
|
zoomOrg = action('&Original size', partial(self.setZoom, 100),
|
|
zoomOrg = action('&Original size', partial(self.setZoom, 100),
|
|
- 'Ctrl+=', 'zoom', u'Zoom to original size', enabled=False)
|
|
|
|
|
|
+ 'Ctrl+=', 'zoom', 'Zoom to original size', enabled=False)
|
|
fitWindow = action('&Fit Window', self.setFitWindow,
|
|
fitWindow = action('&Fit Window', self.setFitWindow,
|
|
- 'Ctrl+F', 'fit-window', u'Zoom follows window size',
|
|
|
|
|
|
+ 'Ctrl+F', 'fit-window', 'Zoom follows window size',
|
|
checkable=True, enabled=False)
|
|
checkable=True, enabled=False)
|
|
fitWidth = action('Fit &Width', self.setFitWidth,
|
|
fitWidth = action('Fit &Width', self.setFitWidth,
|
|
- 'Ctrl+Shift+F', 'fit-width', u'Zoom follows window width',
|
|
|
|
|
|
+ 'Ctrl+Shift+F', 'fit-width', 'Zoom follows window width',
|
|
checkable=True, enabled=False)
|
|
checkable=True, enabled=False)
|
|
# Group zoom controls into a list for easier toggling.
|
|
# Group zoom controls into a list for easier toggling.
|
|
zoomActions = (self.zoomWidget, zoomIn, zoomOut, zoomOrg, fitWindow, fitWidth)
|
|
zoomActions = (self.zoomWidget, zoomIn, zoomOut, zoomOrg, fitWindow, fitWidth)
|
|
@@ -225,15 +229,15 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
}
|
|
}
|
|
|
|
|
|
edit = action('&Edit Label', self.editLabel,
|
|
edit = action('&Edit Label', self.editLabel,
|
|
- 'Ctrl+E', 'edit', u'Modify the label of the selected polygon',
|
|
|
|
|
|
+ 'Ctrl+E', 'edit', 'Modify the label of the selected polygon',
|
|
enabled=False)
|
|
enabled=False)
|
|
self.editButton.setDefaultAction(edit)
|
|
self.editButton.setDefaultAction(edit)
|
|
|
|
|
|
shapeLineColor = action('Shape &Line Color', self.chshapeLineColor,
|
|
shapeLineColor = action('Shape &Line Color', self.chshapeLineColor,
|
|
- icon='color_line', tip=u'Change the line color for this specific shape',
|
|
|
|
|
|
+ icon='color_line', tip='Change the line color for this specific shape',
|
|
enabled=False)
|
|
enabled=False)
|
|
shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor,
|
|
shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor,
|
|
- icon='color', tip=u'Change the fill color for this specific shape',
|
|
|
|
|
|
+ icon='color', tip='Change the fill color for this specific shape',
|
|
enabled=False)
|
|
enabled=False)
|
|
|
|
|
|
labels = self.dock.toggleViewAction()
|
|
labels = self.dock.toggleViewAction()
|
|
@@ -316,30 +320,21 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
|
|
|
# XXX: Could be completely declarative.
|
|
# XXX: Could be completely declarative.
|
|
# Restore application settings.
|
|
# Restore application settings.
|
|
- types = {
|
|
|
|
- 'filename': QString,
|
|
|
|
- 'recentFiles': QStringList,
|
|
|
|
- 'window/size': QSize,
|
|
|
|
- 'window/position': QPoint,
|
|
|
|
- 'window/geometry': QByteArray,
|
|
|
|
- # Docks and toolbars:
|
|
|
|
- 'window/state': QByteArray,
|
|
|
|
- }
|
|
|
|
- self.settings = settings = Settings(types)
|
|
|
|
- self.recentFiles = list(settings['recentFiles'])
|
|
|
|
- size = settings.get('window/size', QSize(600, 500))
|
|
|
|
- position = settings.get('window/position', QPoint(0, 0))
|
|
|
|
|
|
+ self.settings = {}
|
|
|
|
+ self.recentFiles = self.settings.get('recentFiles', [])
|
|
|
|
+ size = self.settings.get('window/size', QSize(600, 500))
|
|
|
|
+ position = self.settings.get('window/position', QPoint(0, 0))
|
|
self.resize(size)
|
|
self.resize(size)
|
|
self.move(position)
|
|
self.move(position)
|
|
# or simply:
|
|
# or simply:
|
|
#self.restoreGeometry(settings['window/geometry']
|
|
#self.restoreGeometry(settings['window/geometry']
|
|
- self.restoreState(settings['window/state'])
|
|
|
|
- self.lineColor = QColor(settings.get('line/color', Shape.line_color))
|
|
|
|
- self.fillColor = QColor(settings.get('fill/color', Shape.fill_color))
|
|
|
|
|
|
+ self.restoreState(self.settings.get('window/state', QByteArray()))
|
|
|
|
+ self.lineColor = QColor(self.settings.get('line/color', Shape.line_color))
|
|
|
|
+ self.fillColor = QColor(self.settings.get('fill/color', Shape.fill_color))
|
|
Shape.line_color = self.lineColor
|
|
Shape.line_color = self.lineColor
|
|
Shape.fill_color = self.fillColor
|
|
Shape.fill_color = self.fillColor
|
|
|
|
|
|
- if settings.get('advanced', QVariant()).toBool():
|
|
|
|
|
|
+ if self.settings.get('advanced', QVariant()):
|
|
self.actions.advancedMode.setChecked(True)
|
|
self.actions.advancedMode.setChecked(True)
|
|
self.toggleAdvancedMode()
|
|
self.toggleAdvancedMode()
|
|
|
|
|
|
@@ -419,8 +414,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.statusBar().showMessage(message, delay)
|
|
self.statusBar().showMessage(message, delay)
|
|
|
|
|
|
def resetState(self):
|
|
def resetState(self):
|
|
- self.itemsToShapes.clear()
|
|
|
|
- self.shapesToItems.clear()
|
|
|
|
|
|
+ self.itemsToShapes = []
|
|
self.labelList.clear()
|
|
self.labelList.clear()
|
|
self.filename = None
|
|
self.filename = None
|
|
self.imageData = None
|
|
self.imageData = None
|
|
@@ -480,7 +474,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
def updateFileMenu(self):
|
|
def updateFileMenu(self):
|
|
current = self.filename
|
|
current = self.filename
|
|
def exists(filename):
|
|
def exists(filename):
|
|
- return os.path.exists(unicode(filename))
|
|
|
|
|
|
+ return os.path.exists(str(filename))
|
|
menu = self.menus.recentFiles
|
|
menu = self.menus.recentFiles
|
|
menu.clear()
|
|
menu.clear()
|
|
files = [f for f in self.recentFiles if f != current and exists(f)]
|
|
files = [f for f in self.recentFiles if f != current and exists(f)]
|
|
@@ -510,7 +504,10 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
else:
|
|
else:
|
|
shape = self.canvas.selectedShape
|
|
shape = self.canvas.selectedShape
|
|
if shape:
|
|
if shape:
|
|
- self.labelList.setItemSelected(self.shapesToItems[shape], True)
|
|
|
|
|
|
+ for item, shape_ in self.itemsToShapes:
|
|
|
|
+ if shape_ == shape:
|
|
|
|
+ break
|
|
|
|
+ item.setSelected(True)
|
|
else:
|
|
else:
|
|
self.labelList.clearSelection()
|
|
self.labelList.clearSelection()
|
|
self.actions.delete.setEnabled(selected)
|
|
self.actions.delete.setEnabled(selected)
|
|
@@ -523,17 +520,17 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
item = QListWidgetItem(shape.label)
|
|
item = QListWidgetItem(shape.label)
|
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
|
item.setCheckState(Qt.Checked)
|
|
item.setCheckState(Qt.Checked)
|
|
- self.itemsToShapes[item] = shape
|
|
|
|
- self.shapesToItems[shape] = item
|
|
|
|
|
|
+ self.itemsToShapes.append((item, shape))
|
|
self.labelList.addItem(item)
|
|
self.labelList.addItem(item)
|
|
for action in self.actions.onShapesPresent:
|
|
for action in self.actions.onShapesPresent:
|
|
action.setEnabled(True)
|
|
action.setEnabled(True)
|
|
|
|
|
|
def remLabel(self, shape):
|
|
def remLabel(self, shape):
|
|
- item = self.shapesToItems[shape]
|
|
|
|
|
|
+ for index, (item, shape_) in enumerate(self.itemsToShapes):
|
|
|
|
+ if shape_ == shape:
|
|
|
|
+ break
|
|
|
|
+ self.itemsToShapes.pop(index)
|
|
self.labelList.takeItem(self.labelList.row(item))
|
|
self.labelList.takeItem(self.labelList.row(item))
|
|
- del self.shapesToItems[shape]
|
|
|
|
- del self.itemsToShapes[item]
|
|
|
|
|
|
|
|
def loadLabels(self, shapes):
|
|
def loadLabels(self, shapes):
|
|
s = []
|
|
s = []
|
|
@@ -553,7 +550,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
def saveLabels(self, filename):
|
|
def saveLabels(self, filename):
|
|
lf = LabelFile()
|
|
lf = LabelFile()
|
|
def format_shape(s):
|
|
def format_shape(s):
|
|
- return dict(label=unicode(s.label),
|
|
|
|
|
|
+ return dict(label=str(s.label),
|
|
line_color=s.line_color.getRgb()\
|
|
line_color=s.line_color.getRgb()\
|
|
if s.line_color != self.lineColor else None,
|
|
if s.line_color != self.lineColor else None,
|
|
fill_color=s.fill_color.getRgb()\
|
|
fill_color=s.fill_color.getRgb()\
|
|
@@ -562,14 +559,14 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
|
|
|
shapes = [format_shape(shape) for shape in self.canvas.shapes]
|
|
shapes = [format_shape(shape) for shape in self.canvas.shapes]
|
|
try:
|
|
try:
|
|
- lf.save(filename, shapes, unicode(self.filename), self.imageData,
|
|
|
|
|
|
+ lf.save(filename, shapes, str(self.filename), self.imageData,
|
|
self.lineColor.getRgb(), self.fillColor.getRgb())
|
|
self.lineColor.getRgb(), self.fillColor.getRgb())
|
|
self.labelFile = lf
|
|
self.labelFile = lf
|
|
self.filename = filename
|
|
self.filename = filename
|
|
return True
|
|
return True
|
|
- except LabelFileError, e:
|
|
|
|
- self.errorMessage(u'Error saving label data',
|
|
|
|
- u'<b>%s</b>' % e)
|
|
|
|
|
|
+ except LabelFileError as e:
|
|
|
|
+ self.errorMessage('Error saving label data',
|
|
|
|
+ '<b>%s</b>' % e)
|
|
return False
|
|
return False
|
|
|
|
|
|
def copySelectedShape(self):
|
|
def copySelectedShape(self):
|
|
@@ -581,13 +578,18 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
item = self.currentItem()
|
|
item = self.currentItem()
|
|
if item and self.canvas.editing():
|
|
if item and self.canvas.editing():
|
|
self._noSelectionSlot = True
|
|
self._noSelectionSlot = True
|
|
- self.canvas.selectShape(self.itemsToShapes[item])
|
|
|
|
|
|
+ for item_, shape in self.itemsToShapes:
|
|
|
|
+ if item_ == item:
|
|
|
|
+ break
|
|
|
|
+ self.canvas.selectShape(shape)
|
|
|
|
|
|
def labelItemChanged(self, item):
|
|
def labelItemChanged(self, item):
|
|
- shape = self.itemsToShapes[item]
|
|
|
|
- label = unicode(item.text())
|
|
|
|
|
|
+ for item_, shape in self.itemsToShapes:
|
|
|
|
+ if item_ == item:
|
|
|
|
+ break
|
|
|
|
+ label = str(item.text())
|
|
if label != shape.label:
|
|
if label != shape.label:
|
|
- shape.label = unicode(item.text())
|
|
|
|
|
|
+ shape.label = str(item.text())
|
|
self.setDirty()
|
|
self.setDirty()
|
|
else: # User probably changed item visibility
|
|
else: # User probably changed item visibility
|
|
self.canvas.setShapeVisible(shape, item.checkState() == Qt.Checked)
|
|
self.canvas.setShapeVisible(shape, item.checkState() == Qt.Checked)
|
|
@@ -642,7 +644,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.adjustScale()
|
|
self.adjustScale()
|
|
|
|
|
|
def togglePolygons(self, value):
|
|
def togglePolygons(self, value):
|
|
- for item, shape in self.itemsToShapes.iteritems():
|
|
|
|
|
|
+ for item, shape in self.itemsToShapes:
|
|
item.setCheckState(Qt.Checked if value else Qt.Unchecked)
|
|
item.setCheckState(Qt.Checked if value else Qt.Unchecked)
|
|
|
|
|
|
def loadFile(self, filename=None):
|
|
def loadFile(self, filename=None):
|
|
@@ -650,16 +652,16 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.resetState()
|
|
self.resetState()
|
|
self.canvas.setEnabled(False)
|
|
self.canvas.setEnabled(False)
|
|
if filename is None:
|
|
if filename is None:
|
|
- filename = self.settings['filename']
|
|
|
|
- filename = unicode(filename)
|
|
|
|
|
|
+ filename = self.settings.get('filename', '')
|
|
|
|
+ filename = str(filename)
|
|
if QFile.exists(filename):
|
|
if QFile.exists(filename):
|
|
if LabelFile.isLabelFile(filename):
|
|
if LabelFile.isLabelFile(filename):
|
|
try:
|
|
try:
|
|
self.labelFile = LabelFile(filename)
|
|
self.labelFile = LabelFile(filename)
|
|
- except LabelFileError, e:
|
|
|
|
- self.errorMessage(u'Error opening file',
|
|
|
|
- (u"<p><b>%s</b></p>"
|
|
|
|
- u"<p>Make sure <i>%s</i> is a valid label file.")\
|
|
|
|
|
|
+ except LabelFileError as e:
|
|
|
|
+ self.errorMessage('Error opening file',
|
|
|
|
+ ("<p><b>%s</b></p>"
|
|
|
|
+ "<p>Make sure <i>%s</i> is a valid label file.")\
|
|
% (e, filename))
|
|
% (e, filename))
|
|
self.status("Error reading %s" % filename)
|
|
self.status("Error reading %s" % filename)
|
|
return False
|
|
return False
|
|
@@ -673,11 +675,11 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.labelFile = None
|
|
self.labelFile = None
|
|
image = QImage.fromData(self.imageData)
|
|
image = QImage.fromData(self.imageData)
|
|
if image.isNull():
|
|
if image.isNull():
|
|
- self.errorMessage(u'Error opening file',
|
|
|
|
- u"<p>Make sure <i>%s</i> is a valid image file." % filename)
|
|
|
|
|
|
+ self.errorMessage('Error opening file',
|
|
|
|
+ "<p>Make sure <i>%s</i> is a valid image file." % filename)
|
|
self.status("Error reading %s" % filename)
|
|
self.status("Error reading %s" % filename)
|
|
return False
|
|
return False
|
|
- self.status("Loaded %s" % os.path.basename(unicode(filename)))
|
|
|
|
|
|
+ self.status("Loaded %s" % os.path.basename(str(filename)))
|
|
self.image = image
|
|
self.image = image
|
|
self.filename = filename
|
|
self.filename = filename
|
|
self.canvas.loadPixmap(QPixmap.fromImage(image))
|
|
self.canvas.loadPixmap(QPixmap.fromImage(image))
|
|
@@ -729,7 +731,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
if not self.mayContinue():
|
|
if not self.mayContinue():
|
|
event.ignore()
|
|
event.ignore()
|
|
s = self.settings
|
|
s = self.settings
|
|
- s['filename'] = self.filename if self.filename else QString()
|
|
|
|
|
|
+ s['filename'] = self.filename if self.filename else ''
|
|
s['window/size'] = self.size()
|
|
s['window/size'] = self.size()
|
|
s['window/position'] = self.pos()
|
|
s['window/position'] = self.pos()
|
|
s['window/state'] = self.saveState()
|
|
s['window/state'] = self.saveState()
|
|
@@ -749,14 +751,14 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
def openFile(self, _value=False):
|
|
def openFile(self, _value=False):
|
|
if not self.mayContinue():
|
|
if not self.mayContinue():
|
|
return
|
|
return
|
|
- path = os.path.dirname(unicode(self.filename))\
|
|
|
|
|
|
+ path = os.path.dirname(str(self.filename))\
|
|
if self.filename else '.'
|
|
if self.filename else '.'
|
|
- formats = ['*.%s' % unicode(fmt).lower()\
|
|
|
|
|
|
+ formats = ['*.%s' % str(fmt).lower()\
|
|
for fmt in QImageReader.supportedImageFormats()]
|
|
for fmt in QImageReader.supportedImageFormats()]
|
|
filters = "Image & Label files (%s)" % \
|
|
filters = "Image & Label files (%s)" % \
|
|
' '.join(formats + ['*%s' % LabelFile.suffix])
|
|
' '.join(formats + ['*%s' % LabelFile.suffix])
|
|
- filename = unicode(QFileDialog.getOpenFileName(self,
|
|
|
|
- '%s - Choose Image or Label file' % __appname__, path, filters))
|
|
|
|
|
|
+ filename = str(QFileDialog.getOpenFileName(self,
|
|
|
|
+ '%s - Choose Image or Label file' % __appname__, path, filters)[0])
|
|
if filename:
|
|
if filename:
|
|
self.loadFile(filename)
|
|
self.loadFile(filename)
|
|
|
|
|
|
@@ -781,15 +783,15 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
dlg = QFileDialog(self, caption, self.currentPath(), filters)
|
|
dlg = QFileDialog(self, caption, self.currentPath(), filters)
|
|
dlg.setDefaultSuffix(LabelFile.suffix[1:])
|
|
dlg.setDefaultSuffix(LabelFile.suffix[1:])
|
|
dlg.setAcceptMode(QFileDialog.AcceptSave)
|
|
dlg.setAcceptMode(QFileDialog.AcceptSave)
|
|
- dlg.setConfirmOverwrite(True)
|
|
|
|
|
|
+ dlg.setOption(QFileDialog.DontConfirmOverwrite, False)
|
|
dlg.setOption(QFileDialog.DontUseNativeDialog, False)
|
|
dlg.setOption(QFileDialog.DontUseNativeDialog, False)
|
|
basename = os.path.splitext(self.filename)[0]
|
|
basename = os.path.splitext(self.filename)[0]
|
|
default_labelfile_name = os.path.join(self.currentPath(),
|
|
default_labelfile_name = os.path.join(self.currentPath(),
|
|
basename + LabelFile.suffix)
|
|
basename + LabelFile.suffix)
|
|
filename = dlg.getSaveFileName(
|
|
filename = dlg.getSaveFileName(
|
|
self, 'Choose File', default_labelfile_name,
|
|
self, 'Choose File', default_labelfile_name,
|
|
- 'Label files (*%s)' % LabelFile.suffix)
|
|
|
|
- return unicode(filename)
|
|
|
|
|
|
+ 'Label files (*%s)' % LabelFile.suffix)[0]
|
|
|
|
+ return str(filename)
|
|
|
|
|
|
def _saveFile(self, filename):
|
|
def _saveFile(self, filename):
|
|
if filename and self.saveLabels(filename):
|
|
if filename and self.saveLabels(filename):
|
|
@@ -810,8 +812,8 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
# Message Dialogs. #
|
|
# Message Dialogs. #
|
|
def hasLabels(self):
|
|
def hasLabels(self):
|
|
if not self.itemsToShapes:
|
|
if not self.itemsToShapes:
|
|
- self.errorMessage(u'No objects labeled',
|
|
|
|
- u'You must label at least one object to save the file.')
|
|
|
|
|
|
+ self.errorMessage('No objects labeled',
|
|
|
|
+ 'You must label at least one object to save the file.')
|
|
return False
|
|
return False
|
|
return True
|
|
return True
|
|
|
|
|
|
@@ -820,18 +822,18 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
|
|
|
def discardChangesDialog(self):
|
|
def discardChangesDialog(self):
|
|
yes, no = QMessageBox.Yes, QMessageBox.No
|
|
yes, no = QMessageBox.Yes, QMessageBox.No
|
|
- msg = u'You have unsaved changes, proceed anyway?'
|
|
|
|
- return yes == QMessageBox.warning(self, u'Attention', msg, yes|no)
|
|
|
|
|
|
+ msg = 'You have unsaved changes, proceed anyway?'
|
|
|
|
+ return yes == QMessageBox.warning(self, 'Attention', msg, yes|no)
|
|
|
|
|
|
def errorMessage(self, title, message):
|
|
def errorMessage(self, title, message):
|
|
return QMessageBox.critical(self, title,
|
|
return QMessageBox.critical(self, title,
|
|
'<p><b>%s</b></p>%s' % (title, message))
|
|
'<p><b>%s</b></p>%s' % (title, message))
|
|
|
|
|
|
def currentPath(self):
|
|
def currentPath(self):
|
|
- return os.path.dirname(unicode(self.filename)) if self.filename else '.'
|
|
|
|
|
|
+ return os.path.dirname(str(self.filename)) if self.filename else '.'
|
|
|
|
|
|
def chooseColor1(self):
|
|
def chooseColor1(self):
|
|
- color = self.colorDialog.getColor(self.lineColor, u'Choose line color',
|
|
|
|
|
|
+ color = self.colorDialog.getColor(self.lineColor, 'Choose line color',
|
|
default=DEFAULT_LINE_COLOR)
|
|
default=DEFAULT_LINE_COLOR)
|
|
if color:
|
|
if color:
|
|
self.lineColor = color
|
|
self.lineColor = color
|
|
@@ -841,7 +843,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.setDirty()
|
|
self.setDirty()
|
|
|
|
|
|
def chooseColor2(self):
|
|
def chooseColor2(self):
|
|
- color = self.colorDialog.getColor(self.fillColor, u'Choose fill color',
|
|
|
|
|
|
+ color = self.colorDialog.getColor(self.fillColor, 'Choose fill color',
|
|
default=DEFAULT_FILL_COLOR)
|
|
default=DEFAULT_FILL_COLOR)
|
|
if color:
|
|
if color:
|
|
self.fillColor = color
|
|
self.fillColor = color
|
|
@@ -851,8 +853,8 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
|
|
|
def deleteSelectedShape(self):
|
|
def deleteSelectedShape(self):
|
|
yes, no = QMessageBox.Yes, QMessageBox.No
|
|
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):
|
|
|
|
|
|
+ msg = 'You are about to permanently delete this polygon, proceed anyway?'
|
|
|
|
+ if yes == QMessageBox.warning(self, 'Attention', msg, yes|no):
|
|
self.remLabel(self.canvas.deleteSelected())
|
|
self.remLabel(self.canvas.deleteSelected())
|
|
self.setDirty()
|
|
self.setDirty()
|
|
if self.noShapes():
|
|
if self.noShapes():
|
|
@@ -860,7 +862,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
action.setEnabled(False)
|
|
action.setEnabled(False)
|
|
|
|
|
|
def chshapeLineColor(self):
|
|
def chshapeLineColor(self):
|
|
- color = self.colorDialog.getColor(self.lineColor, u'Choose line color',
|
|
|
|
|
|
+ color = self.colorDialog.getColor(self.lineColor, 'Choose line color',
|
|
default=DEFAULT_LINE_COLOR)
|
|
default=DEFAULT_LINE_COLOR)
|
|
if color:
|
|
if color:
|
|
self.canvas.selectedShape.line_color = color
|
|
self.canvas.selectedShape.line_color = color
|
|
@@ -868,7 +870,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
self.setDirty()
|
|
self.setDirty()
|
|
|
|
|
|
def chshapeFillColor(self):
|
|
def chshapeFillColor(self):
|
|
- color = self.colorDialog.getColor(self.fillColor, u'Choose fill color',
|
|
|
|
|
|
+ color = self.colorDialog.getColor(self.fillColor, 'Choose fill color',
|
|
default=DEFAULT_FILL_COLOR)
|
|
default=DEFAULT_FILL_COLOR)
|
|
if color:
|
|
if color:
|
|
self.canvas.selectedShape.fill_color = color
|
|
self.canvas.selectedShape.fill_color = color
|