def __init__(self, text="Enter object label", parent=None, listItem=None): super(LabelDialog, self).__init__(parent) self.edit = QLineEdit() self.edit.setText(text) self.edit.setValidator(labelValidator()) self.edit.editingFinished.connect(self.postProcess) layout = QVBoxLayout() layout.addWidget(self.edit) self.buttonBox = bb = BB(BB.Ok | BB.Cancel, Qt.Horizontal, self) bb.button(BB.Ok).setIcon(newIcon('done')) bb.button(BB.Cancel).setIcon(newIcon('undo')) bb.accepted.connect(self.validate) bb.rejected.connect(self.reject) layout.addWidget(bb) self.listItem = listItem[:] if listItem is not None and len(listItem) > 0: self.listWidget = QListWidget(self) for item in listItem: #qDebug(item) self.listWidget.addItem(item) self.listWidget.itemDoubleClicked.connect(self.listItemClick) layout.addWidget(self.listWidget) # add a shortcut to choose the first label action = partial(newAction, self) choose1 = action('@Choose label', self.choose, ' ', None, u'Choose label') addActions(self.listWidget, (choose1, )) self.setLayout(layout)
def toolbar(self, title, actions=None): toolbar = ToolBar(title) toolbar.setObjectName(u'%sToolBar' % title) #toolbar.setOrientation(Qt.Vertical) toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) if actions: addActions(toolbar, actions) self.addToolBar(Qt.LeftToolBarArea, toolbar) return toolbar
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) self.menus.edit.clear() actions = (self.actions.create,) if self.beginner()\ else (self.actions.createMode, self.actions.editMode) addActions(self.menus.edit, actions + self.actions.editMenu)
def __init__(self, defaultFilename=None): super(MainWindow, self).__init__() self.setWindowTitle(__appname__) # Save as Pascal voc xml self.defaultSaveDir = None self.usingPascalVocFormat = True if self.usingPascalVocFormat: LabelFile.suffix = '.xml' # For loading all image under a directory self.mImgList = [] self.dirname = None self.labelHist = [] self.lastOpenDir = None # Whether we need to save or not. self.dirty = False # Enble auto saving if pressing next self.autoSaving = True self._noSelectionSlot = False self._beginner = True self.screencastViewer = "firefox" self.screencast = "https://youtu.be/p0nR2YsCY_U" self.loadPredefinedClasses() # Main widgets and related state. self.labelDialog = LabelDialog(parent=self, listItem=self.labelHist) self.labelList = QListWidget() self.itemsToShapes = {} self.shapesToItems = {} self.labelList.itemActivated.connect(self.labelSelectionChanged) self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged) self.labelList.itemDoubleClicked.connect(self.editLabel) # 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'Box Labels', self) self.dock.setObjectName(u'Labels') self.dock.setWidget(self.labelListContainer) # Tzutalin 20160906 : Add file list and dock to move faster self.fileListWidget = QListWidget() self.fileListWidget.itemDoubleClicked.connect( self.fileitemDoubleClicked) filelistLayout = QVBoxLayout() filelistLayout.setContentsMargins(0, 0, 0, 0) filelistLayout.addWidget(self.fileListWidget) self.fileListContainer = QWidget() self.fileListContainer.setLayout(filelistLayout) self.filedock = QDockWidget(u'File List', self) self.filedock.setObjectName(u'Files') self.filedock.setWidget(self.fileListContainer) self.zoomWidget = ZoomWidget() self.colorDialog = ColorDialog(parent=self) self.canvas = Canvas() self.canvas.zoomRequest.connect(self.zoomRequest) scroll = QScrollArea() scroll.setWidget(self.canvas) scroll.setWidgetResizable(True) self.scrollBars = { Qt.Vertical: scroll.verticalScrollBar(), Qt.Horizontal: scroll.horizontalScrollBar() } self.canvas.scrollRequest.connect(self.scrollRequest) self.canvas.newShape.connect(self.newShape) self.canvas.shapeMoved.connect(self.setDirty) self.canvas.selectionChanged.connect(self.shapeSelectionChanged) self.canvas.drawingPolygon.connect(self.toggleDrawingSensitive) self.setCentralWidget(scroll) self.addDockWidget(Qt.RightDockWidgetArea, self.dock) # Tzutalin 20160906 : Add file list and dock to move faster self.addDockWidget(Qt.RightDockWidgetArea, self.filedock) self.dockFeatures = QDockWidget.DockWidgetClosable\ | QDockWidget.DockWidgetFloatable self.dock.setFeatures(self.dock.features() ^ self.dockFeatures) # Actions action = partial(newAction, self) quit = action('&Quit', self.close, 'Ctrl+Q', 'quit', u'Quit application') open = action('&Open', self.openFile, 'Ctrl+O', 'open', u'Open image or label file') opendir = action('&Open Dir', self.openDir, 'Ctrl+u', 'open', u'Open Dir') changeSavedir = action('&Change default saved Annotation dir', self.changeSavedir, 'Ctrl+r', 'open', u'Change default saved Annotation dir') openAnnotation = action('&Open Annotation', self.openAnnotation, 'Ctrl+q', 'openAnnotation', u'Open Annotation') openNextImg = action('&Next Image', self.openNextImg, 'd', 'next', u'Open Next') openPrevImg = action('&Prev Image', self.openPrevImg, 'a', 'prev', u'Open Prev') save = action('&Save', self.saveFile, 'Ctrl+S', 'save', u'Save labels to file', enabled=False) saveAs = action('&Save As', self.saveFileAs, 'Ctrl+Shift+S', 'save-as', u'Save labels to a different file', enabled=False) close = action('&Close', self.closeFile, 'Ctrl+W', 'close', u'Close current file') color1 = action('Box &Line Color', self.chooseColor1, 'Ctrl+L', 'color_line', u'Choose Box line color') color2 = action('Box &Fill Color', self.chooseColor2, 'Ctrl+Shift+L', 'color', u'Choose Box fill color') createMode = action('Create\nRectBox', self.setCreateMode, 'Ctrl+N', 'new', u'Start drawing Boxs', enabled=False) editMode = action('&Edit\nRectBox', self.setEditMode, 'Ctrl+J', 'edit', u'Move and edit Boxs', enabled=False) create = action('Create\nRectBox', self.createShape, 'w', 'new', u'Draw a new Box', enabled=False) delete = action('Delete\nRectBox', self.deleteSelectedShape, 'Delete', 'delete', u'Delete', enabled=False) copy = action('&Duplicate\nRectBox', self.copySelectedShape, 'Ctrl+D', 'copy', u'Create a duplicate of the selected Box', enabled=False) advancedMode = action('&Advanced Mode', self.toggleAdvancedMode, 'Ctrl+Shift+A', 'expert', u'Switch to advanced mode', checkable=True) hideAll = action('&Hide\nRectBox', partial(self.togglePolygons, False), 'Ctrl+H', 'hide', u'Hide all Boxs', enabled=False) showAll = action('&Show\nRectBox', partial(self.togglePolygons, True), 'Ctrl+A', 'hide', u'Show all Boxs', enabled=False) help = action('&Tutorial', self.tutorial, 'Ctrl+T', 'help', u'Show demos') zoom = QWidgetAction(self) zoom.setDefaultWidget(self.zoomWidget) self.zoomWidget.setWhatsThis( u"Zoom in or out of the image. Also accessible with"\ " %s and %s from the canvas." % (fmtShortcut("Ctrl+[-+]"), fmtShortcut("Ctrl+Wheel"))) self.zoomWidget.setEnabled(False) zoomIn = action('Zoom &In', partial(self.addZoom, 10), 'Ctrl++', 'zoom-in', u'Increase zoom level', enabled=False) zoomOut = action('&Zoom Out', partial(self.addZoom, -10), 'Ctrl+-', 'zoom-out', u'Decrease zoom level', enabled=False) zoomOrg = action('&Original size', partial(self.setZoom, 100), 'Ctrl+=', 'zoom', u'Zoom to original size', enabled=False) fitWindow = action('&Fit Window', self.setFitWindow, 'Ctrl+F', 'fit-window', u'Zoom follows window size', checkable=True, enabled=False) fitWidth = action('Fit &Width', self.setFitWidth, 'Ctrl+Shift+F', 'fit-width', u'Zoom follows window width', checkable=True, enabled=False) # Group zoom controls into a list for easier toggling. zoomActions = (self.zoomWidget, zoomIn, zoomOut, zoomOrg, fitWindow, fitWidth) self.zoomMode = self.MANUAL_ZOOM self.scalers = { self.FIT_WINDOW: self.scaleFitWindow, self.FIT_WIDTH: self.scaleFitWidth, # Set to one to scale to 100% when loading files. self.MANUAL_ZOOM: lambda: 1, } edit = action('&Edit Label', self.editLabel, 'Ctrl+E', 'edit', u'Modify the label of the selected Box', enabled=False) self.editButton.setDefaultAction(edit) shapeLineColor = action( 'Shape &Line Color', self.chshapeLineColor, icon='color_line', tip=u'Change the line color for this specific shape', enabled=False) shapeFillColor = action( 'Shape &Fill Color', self.chshapeFillColor, icon='color', tip=u'Change the fill color for this specific shape', enabled=False) labels = self.dock.toggleViewAction() labels.setText('Show/Hide Label Panel') labels.setShortcut('Ctrl+Shift+L') # Lavel list context menu. labelMenu = QMenu() addActions(labelMenu, (edit, delete)) self.labelList.setContextMenuPolicy(Qt.CustomContextMenu) self.labelList.customContextMenuRequested.connect( self.popLabelListMenu) # Store actions for further handling. self.actions = struct( save=save, saveAs=saveAs, open=open, close=close, lineColor=color1, fillColor=color2, 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, opendir, save, saveAs, close, quit), beginner=(), advanced=(), editMenu=(edit, copy, delete, None, color1, color2), beginnerContext=(create, edit, copy, delete), advancedContext=(createMode, editMode, edit, copy, delete, shapeLineColor, shapeFillColor), onLoadActive=(close, create, createMode, editMode), onShapesPresent=(saveAs, hideAll, showAll)) self.menus = struct(file=self.menu('&File'), edit=self.menu('&Edit'), view=self.menu('&View'), help=self.menu('&Help'), recentFiles=QMenu('Open &Recent'), labelList=labelMenu) addActions(self.menus.file, (open, opendir, changeSavedir, openAnnotation, self.menus.recentFiles, save, saveAs, close, None, quit)) addActions(self.menus.help, (help, )) addActions(self.menus.view, (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') self.actions.beginner = (open, opendir, openNextImg, openPrevImg, save, None, create, copy, delete, None, zoomIn, zoom, zoomOut, fitWindow, fitWidth) self.actions.advanced = (open, save, None, createMode, editMode, None, hideAll, showAll) self.statusBar().showMessage('%s started.' % __appname__) self.statusBar().show() # Application state. self.image = QImage() self.filePath = u(defaultFilename) self.recentFiles = [] self.maxRecent = 7 self.lineColor = None self.fillColor = None self.zoom_level = 100 self.fit_window = False # XXX: Could be completely declarative. # Restore application settings. if have_qstring(): types = { 'filename': QString, 'recentFiles': QStringList, 'window/size': QSize, 'window/position': QPoint, 'window/geometry': QByteArray, 'line/color': QColor, 'fill/color': QColor, 'advanced': bool, # Docks and toolbars: 'window/state': QByteArray, 'savedir': QString, 'lastOpenDir': QString, } else: types = { 'filename': str, 'recentFiles': list, 'window/size': QSize, 'window/position': QPoint, 'window/geometry': QByteArray, 'line/color': QColor, 'fill/color': QColor, 'advanced': bool, # Docks and toolbars: 'window/state': QByteArray, 'savedir': str, 'lastOpenDir': str, } self.settings = settings = Settings(types) self.recentFiles = list(settings.get('recentFiles', [])) size = settings.get('window/size', QSize(600, 500)) position = settings.get('window/position', QPoint(0, 0)) self.resize(size) self.move(position) saveDir = u(settings.get('savedir', None)) self.lastOpenDir = u(settings.get('lastOpenDir', None)) if os.path.exists(saveDir): self.defaultSaveDir = saveDir self.statusBar().showMessage( '%s started. Annotation will be saved to %s' % (__appname__, self.defaultSaveDir)) self.statusBar().show() # or simply: #self.restoreGeometry(settings['window/geometry'] self.restoreState(settings.get('window/state', QByteArray())) self.lineColor = QColor(settings.get('line/color', Shape.line_color)) self.fillColor = QColor(settings.get('fill/color', Shape.fill_color)) Shape.line_color = self.lineColor Shape.fill_color = self.fillColor def xbool(x): if isinstance(x, QVariant): return x.toBool() return bool(x) if xbool(settings.get('advanced', False)): 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. self.queueEvent(partial(self.loadFile, self.filePath)) # Callbacks: self.zoomWidget.valueChanged.connect(self.paintCanvas) self.populateModeActions()
def menu(self, title, actions=None): menu = self.menuBar().addMenu(title) if actions: addActions(menu, actions) return menu
def setAdvanced(self): self.tools.clear() addActions(self.tools, self.actions.advanced)
def setBeginner(self): self.tools.clear() addActions(self.tools, self.actions.beginner)
def __init__(self, filename=None, output=None): super(MainWindow, self).__init__() self.setWindowTitle(__appname__) # Whether we need to save or not. self.dirty = False self._noSelectionSlot = False self._beginner = True self.screencastViewer = "firefox" self.screencast = "screencast.ogv" # Main widgets and related state. self.labelDialog = LabelDialog(parent=self) self.labelList = QListWidget() self.itemsToShapes = [] self.labelList.itemActivated.connect(self.labelSelectionChanged) self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged) self.labelList.itemDoubleClicked.connect(self.editLabel) # 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) #define table view self.table_view = QTableView(self) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.show() self.table_view.doubleClicked.connect(self.openCurrentData) listLayout.addWidget(self.table_view) self.dock = QDockWidget('Polygon Labels', self) self.dock.setObjectName('Labels') self.dock.setWidget(self.labelListContainer) #self.dock.setWidget(self.infoLayout) self.zoomWidget = ZoomWidget() self.colorDialog = ColorDialog(parent=self) self.canvas = Canvas() self.canvas.zoomRequest.connect(self.zoomRequest) scroll = QScrollArea() scroll.setWidget(self.canvas) scroll.setWidgetResizable(True) self.scrollBars = { Qt.Vertical: scroll.verticalScrollBar(), Qt.Horizontal: scroll.horizontalScrollBar() } self.canvas.scrollRequest.connect(self.scrollRequest) self.canvas.newShape.connect(self.newShape) self.canvas.shapeMoved.connect(self.setDirty) self.canvas.selectionChanged.connect(self.shapeSelectionChanged) self.canvas.drawingPolygon.connect(self.toggleDrawingSensitive) 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) quit = action('&Quit', self.close, 'Ctrl+Q', 'quit', 'Quit application') open = action('&Open', self.openFile, 'Ctrl+O', 'open', 'Open image or label file') save = action('&Save', self.saveFile, 'Ctrl+S', 'save', 'Save labels to file', enabled=False) saveAs = action('&Save As', self.saveFileAs, 'Ctrl+Shift+S', 'save-as', 'Save labels to a different file', enabled=False) close = action('&Close', self.closeFile, 'Ctrl+W', 'close', 'Close current file') color1 = action('Polygon &Line Color', self.chooseColor1, 'Ctrl+L', 'color_line', 'Choose polygon line color') color2 = action('Polygon &Fill Color', self.chooseColor2, 'Ctrl+Shift+L', 'color', 'Choose polygon fill color') createMode = action('Create\nPolygo&ns', self.setCreateMode, 'Ctrl+N', 'new', 'Start drawing polygons', enabled=False) editMode = action('&Edit\nPolygons', self.setEditMode, 'Ctrl+J', 'edit', 'Move and edit polygons', enabled=False) create = action('Create\nPolygo&n', self.createShape, 'Ctrl+N', 'new', 'Draw a new polygon', enabled=False) delete = action('Delete\nPolygon', self.deleteSelectedShape, 'Delete', 'delete', 'Delete', enabled=False) copy = action('&Duplicate\nPolygon', self.copySelectedShape, 'Ctrl+D', 'copy', 'Create a duplicate of the selected polygon', enabled=False) advancedMode = action('&Advanced Mode', self.toggleAdvancedMode, 'Ctrl+Shift+A', 'expert', 'Switch to advanced mode', checkable=True) hideAll = action('&Hide\nPolygons', partial(self.togglePolygons, False), 'Ctrl+H', 'hide', 'Hide all polygons', enabled=False) showAll = action('&Show\nPolygons', partial(self.togglePolygons, True), 'Ctrl+A', 'hide', 'Show all polygons', enabled=False) help = action('&Tutorial', self.tutorial, 'Ctrl+T', 'help', 'Show screencast of introductory tutorial') zoom = QWidgetAction(self) zoom.setDefaultWidget(self.zoomWidget) self.zoomWidget.setWhatsThis( "Zoom in or out of the image. Also accessible with"\ " %s and %s from the canvas." % (fmtShortcut("Ctrl+[-+]"), fmtShortcut("Ctrl+Wheel"))) self.zoomWidget.setEnabled(False) zoomIn = action('Zoom &In', partial(self.addZoom, 10), 'Ctrl++', 'zoom-in', 'Increase zoom level', enabled=False) zoomOut = action('&Zoom Out', partial(self.addZoom, -10), 'Ctrl+-', 'zoom-out', 'Decrease zoom level', enabled=False) zoomOrg = action('&Original size', partial(self.setZoom, 100), 'Ctrl+=', 'zoom', 'Zoom to original size', enabled=False) fitWindow = action('&Fit Window', self.setFitWindow, 'Ctrl+F', 'fit-window', 'Zoom follows window size', checkable=True, enabled=False) fitWidth = action('Fit &Width', self.setFitWidth, 'Ctrl+Shift+F', 'fit-width', 'Zoom follows window width', checkable=True, enabled=False) # Group zoom controls into a list for easier toggling. zoomActions = (self.zoomWidget, zoomIn, zoomOut, zoomOrg, fitWindow, fitWidth) self.zoomMode = self.MANUAL_ZOOM self.scalers = { self.FIT_WINDOW: self.scaleFitWindow, self.FIT_WIDTH: self.scaleFitWidth, # Set to one to scale to 100% when loading files. self.MANUAL_ZOOM: lambda: 1, } edit = action('&Edit Label', self.editLabel, 'Ctrl+E', 'edit', 'Modify the label of the selected polygon', enabled=False) self.editButton.setDefaultAction(edit) shapeLineColor = action('Shape &Line Color', self.chshapeLineColor, icon='color_line', tip='Change the line color for this specific shape', enabled=False) shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor, icon='color', tip='Change the fill color for this specific shape', enabled=False) labels = self.dock.toggleViewAction() labels.setText('Show/Hide Label Panel') labels.setShortcut('Ctrl+Shift+L') # Lavel list context menu. labelMenu = QMenu() addActions(labelMenu, (edit, delete)) self.labelList.setContextMenuPolicy(Qt.CustomContextMenu) self.labelList.customContextMenuRequested.connect(self.popLabelListMenu) # Store actions for further handling. self.actions = struct(save=save, saveAs=saveAs, open=open, close=close, lineColor=color1, fillColor=color2, 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,saveAs,close,quit), beginner=(), advanced=(), editMenu=(edit, copy, delete, None, color1, color2), beginnerContext=(create, edit, copy, delete), advancedContext=(createMode, editMode, edit, copy, delete, shapeLineColor, shapeFillColor), onLoadActive=(close, create, createMode, editMode), onShapesPresent=(saveAs, hideAll, showAll)) self.menus = struct( file=self.menu('&File'), edit=self.menu('&Edit'), view=self.menu('&View'), help=self.menu('&Help'), recentFiles=QMenu('Open &Recent'), labelList=labelMenu) addActions(self.menus.file, (open, self.menus.recentFiles, save, saveAs, close, None, quit)) addActions(self.menus.help, (help,)) addActions(self.menus.view, ( 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') self.actions.beginner = ( open, save, None, create, copy, delete, None, zoomIn, zoom, zoomOut, fitWindow, fitWidth) self.actions.advanced = ( open, save, None, createMode, editMode, None, hideAll, showAll) self.statusBar().showMessage('%s started.' % __appname__) self.statusBar().show() # Application state. self.image = QImage() self.filename = filename self.labeling_once = output is not None self.output = output self.recentFiles = [] self.maxRecent = 7 self.lineColor = None self.fillColor = None self.zoom_level = 100 self.fit_window = False # XXX: Could be completely declarative. # Restore application settings. self.settings = {} self.recentFiles = self.settings.get('recentFiles', []) size = self.settings.get('window/size', QSize(1000, 800)) position = self.settings.get('window/position', QPoint(0, 0)) self.resize(size) self.move(position) # or simply: #self.restoreGeometry(settings['window/geometry'] 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.fill_color = self.fillColor if self.settings.get('advanced', QVariant()): 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. self.queueEvent(partial(self.loadFile, self.filename)) # Callbacks: self.zoomWidget.valueChanged.connect(self.paintCanvas) self.populateModeActions()
def __init__(self, filename=None): super(MainWindow, self).__init__() self.setWindowTitle(__appname__) # Save as Pascal voc xml self.defaultSaveDir = None self.usingPascalVocFormat = True if self.usingPascalVocFormat: LabelFile.suffix = '.xml' # For loading all image under a directory self.mImgList = [] self.dirname = None self.labelHist = [] self.lastOpenDir = None # Whether we need to save or not. self.dirty = False # Enble auto saving if pressing next self.autoSaving = True self._noSelectionSlot = False self._beginner = True self.screencastViewer = "firefox" self.screencast = "https://youtu.be/p0nR2YsCY_U" self.loadPredefinedClasses() # Main widgets and related state. self.labelDialog = LabelDialog(parent=self, listItem=self.labelHist) self.labelList = QListWidget() self.itemsToShapes = {} self.shapesToItems = {} self.labelList.itemActivated.connect(self.labelSelectionChanged) self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged) self.labelList.itemDoubleClicked.connect(self.editLabel) # 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'Box Labels', self) self.dock.setObjectName(u'Labels') self.dock.setWidget(self.labelListContainer) # Tzutalin 20160906 : Add file list and dock to move faster self.fileListWidget = QListWidget() self.fileListWidget.itemDoubleClicked.connect(self.fileitemDoubleClicked) filelistLayout = QVBoxLayout() filelistLayout.setContentsMargins(0, 0, 0, 0) filelistLayout.addWidget(self.fileListWidget) self.fileListContainer = QWidget() self.fileListContainer.setLayout(filelistLayout) self.filedock = QDockWidget(u'File List', self) self.filedock.setObjectName(u'Files') self.filedock.setWidget(self.fileListContainer) self.zoomWidget = ZoomWidget() self.colorDialog = ColorDialog(parent=self) self.canvas = Canvas() self.canvas.zoomRequest.connect(self.zoomRequest) scroll = QScrollArea() scroll.setWidget(self.canvas) scroll.setWidgetResizable(True) self.scrollBars = { Qt.Vertical: scroll.verticalScrollBar(), Qt.Horizontal: scroll.horizontalScrollBar() } self.canvas.scrollRequest.connect(self.scrollRequest) self.canvas.newShape.connect(self.newShape) self.canvas.shapeMoved.connect(self.setDirty) self.canvas.selectionChanged.connect(self.shapeSelectionChanged) self.canvas.drawingPolygon.connect(self.toggleDrawingSensitive) self.setCentralWidget(scroll) self.addDockWidget(Qt.RightDockWidgetArea, self.dock) # Tzutalin 20160906 : Add file list and dock to move faster self.addDockWidget(Qt.RightDockWidgetArea, self.filedock) self.dockFeatures = QDockWidget.DockWidgetClosable\ | QDockWidget.DockWidgetFloatable self.dock.setFeatures(self.dock.features() ^ self.dockFeatures) # Actions action = partial(newAction, self) quit = action('&Quit', self.close, 'Ctrl+Q', 'quit', u'Quit application') open = action('&Open', self.openFile, 'Ctrl+O', 'open', u'Open image or label file') opendir = action('&Open Dir', self.openDir, 'Ctrl+u', 'open', u'Open Dir') changeSavedir = action('&Change default saved Annotation dir', self.changeSavedir, 'Ctrl+r', 'open', u'Change default saved Annotation dir') openAnnotation = action('&Open Annotation', self.openAnnotation, 'Ctrl+q', 'openAnnotation', u'Open Annotation') openNextImg = action('&Next Image', self.openNextImg, 'd', 'next', u'Open Next') openPrevImg = action('&Prev Image', self.openPrevImg, 'a', 'prev', u'Open Prev') save = action('&Save', self.saveFile, 'Ctrl+S', 'save', u'Save labels to file', enabled=False) saveAs = action('&Save As', self.saveFileAs, 'Ctrl+Shift+S', 'save-as', u'Save labels to a different file', enabled=False) close = action('&Close', self.closeFile, 'Ctrl+W', 'close', u'Close current file') color1 = action('Box &Line Color', self.chooseColor1, 'Ctrl+L', 'color_line', u'Choose Box line color') color2 = action('Box &Fill Color', self.chooseColor2, 'Ctrl+Shift+L', 'color', u'Choose Box fill color') createMode = action('Create\nRectBox', self.setCreateMode, 'Ctrl+N', 'new', u'Start drawing Boxs', enabled=False) editMode = action('&Edit\nRectBox', self.setEditMode, 'Ctrl+J', 'edit', u'Move and edit Boxs', enabled=False) create = action('Create\nRectBox', self.createShape, 'w', 'new', u'Draw a new Box', enabled=False) delete = action('Delete\nRectBox', self.deleteSelectedShape, 'Delete', 'delete', u'Delete', enabled=False) copy = action('&Duplicate\nRectBox', self.copySelectedShape, 'Ctrl+D', 'copy', u'Create a duplicate of the selected Box', enabled=False) advancedMode = action('&Advanced Mode', self.toggleAdvancedMode, 'Ctrl+Shift+A', 'expert', u'Switch to advanced mode', checkable=True) hideAll = action('&Hide\nRectBox', partial(self.togglePolygons, False), 'Ctrl+H', 'hide', u'Hide all Boxs', enabled=False) showAll = action('&Show\nRectBox', partial(self.togglePolygons, True), 'Ctrl+A', 'hide', u'Show all Boxs', enabled=False) help = action('&Tutorial', self.tutorial, 'Ctrl+T', 'help', u'Show demos') zoom = QWidgetAction(self) zoom.setDefaultWidget(self.zoomWidget) self.zoomWidget.setWhatsThis( u"Zoom in or out of the image. Also accessible with"\ " %s and %s from the canvas." % (fmtShortcut("Ctrl+[-+]"), fmtShortcut("Ctrl+Wheel"))) self.zoomWidget.setEnabled(False) zoomIn = action('Zoom &In', partial(self.addZoom, 10), 'Ctrl++', 'zoom-in', u'Increase zoom level', enabled=False) zoomOut = action('&Zoom Out', partial(self.addZoom, -10), 'Ctrl+-', 'zoom-out', u'Decrease zoom level', enabled=False) zoomOrg = action('&Original size', partial(self.setZoom, 100), 'Ctrl+=', 'zoom', u'Zoom to original size', enabled=False) fitWindow = action('&Fit Window', self.setFitWindow, 'Ctrl+F', 'fit-window', u'Zoom follows window size', checkable=True, enabled=False) fitWidth = action('Fit &Width', self.setFitWidth, 'Ctrl+Shift+F', 'fit-width', u'Zoom follows window width', checkable=True, enabled=False) # Group zoom controls into a list for easier toggling. zoomActions = (self.zoomWidget, zoomIn, zoomOut, zoomOrg, fitWindow, fitWidth) self.zoomMode = self.MANUAL_ZOOM self.scalers = { self.FIT_WINDOW: self.scaleFitWindow, self.FIT_WIDTH: self.scaleFitWidth, # Set to one to scale to 100% when loading files. self.MANUAL_ZOOM: lambda: 1, } edit = action('&Edit Label', self.editLabel, 'Ctrl+E', 'edit', u'Modify the label of the selected Box', enabled=False) self.editButton.setDefaultAction(edit) shapeLineColor = action('Shape &Line Color', self.chshapeLineColor, icon='color_line', tip=u'Change the line color for this specific shape', enabled=False) shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor, icon='color', tip=u'Change the fill color for this specific shape', enabled=False) labels = self.dock.toggleViewAction() labels.setText('Show/Hide Label Panel') labels.setShortcut('Ctrl+Shift+L') # Lavel list context menu. labelMenu = QMenu() addActions(labelMenu, (edit, delete)) self.labelList.setContextMenuPolicy(Qt.CustomContextMenu) self.labelList.customContextMenuRequested.connect(self.popLabelListMenu) # Store actions for further handling. self.actions = struct(save=save, saveAs=saveAs, open=open, close=close, lineColor=color1, fillColor=color2, 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,opendir,save,saveAs,close,quit), beginner=(), advanced=(), editMenu=(edit, copy, delete, None, color1, color2), beginnerContext=(create, edit, copy, delete), advancedContext=(createMode, editMode, edit, copy, delete, shapeLineColor, shapeFillColor), onLoadActive=(close, create, createMode, editMode), onShapesPresent=(saveAs, hideAll, showAll)) self.menus = struct( file=self.menu('&File'), edit=self.menu('&Edit'), view=self.menu('&View'), help=self.menu('&Help'), recentFiles=QMenu('Open &Recent'), labelList=labelMenu) addActions(self.menus.file, (open, opendir,changeSavedir, openAnnotation, self.menus.recentFiles, save, saveAs, close, None, quit)) addActions(self.menus.help, (help,)) addActions(self.menus.view, ( 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') self.actions.beginner = ( open, opendir, openNextImg, openPrevImg, save, None, create, copy, delete, None, zoomIn, zoom, zoomOut, fitWindow, fitWidth) self.actions.advanced = ( open, save, None, createMode, editMode, None, hideAll, showAll) self.statusBar().showMessage('%s started.' % __appname__) self.statusBar().show() # Application state. self.image = QImage() self.filename = filename self.recentFiles = [] self.maxRecent = 7 self.lineColor = None self.fillColor = None self.zoom_level = 100 self.fit_window = False # XXX: Could be completely declarative. # Restore application settings. if have_qstring(): types = { 'filename': QString, 'recentFiles': QStringList, 'window/size': QSize, 'window/position': QPoint, 'window/geometry': QByteArray, 'line/color': QColor, 'fill/color': QColor, 'advanced': bool, # Docks and toolbars: 'window/state': QByteArray, 'savedir': QString, 'lastOpenDir': QString, } else: types = { 'filename': str, 'recentFiles': list, 'window/size': QSize, 'window/position': QPoint, 'window/geometry': QByteArray, 'line/color': QColor, 'fill/color': QColor, 'advanced': bool, # Docks and toolbars: 'window/state': QByteArray, 'savedir': str, 'lastOpenDir': str, } self.settings = settings = Settings(types) self.recentFiles = list(settings.get('recentFiles', [])) size = settings.get('window/size', QSize(600, 500)) position = settings.get('window/position', QPoint(0, 0)) self.resize(size) self.move(position) saveDir = settings.get('savedir', None) self.lastOpenDir = settings.get('lastOpenDir', None) if os.path.exists(str(saveDir)): self.defaultSaveDir = str(saveDir) self.statusBar().showMessage('%s started. Annotation will be saved to %s' %(__appname__, self.defaultSaveDir)) self.statusBar().show() # or simply: #self.restoreGeometry(settings['window/geometry'] self.restoreState(settings.get('window/state', QByteArray())) self.lineColor = QColor(settings.get('line/color', Shape.line_color)) self.fillColor = QColor(settings.get('fill/color', Shape.fill_color)) Shape.line_color = self.lineColor Shape.fill_color = self.fillColor def xbool(x): if isinstance(x, QVariant): return x.toBool() return bool(x) if xbool(settings.get('advanced', False)): 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. self.queueEvent(partial(self.loadFile, self.filename)) # Callbacks: self.zoomWidget.valueChanged.connect(self.paintCanvas) self.populateModeActions()