Move icons to resource folder, fix the unicode issue, support zh-tw lang

This commit is contained in:
tzutalin 2018-12-01 00:20:46 -08:00
parent 13a700ad53
commit eaac031404
55 changed files with 371 additions and 125 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
icons/.DS_Store resources/icons/.DS_Store
resources.py resources.py

View File

@ -32,6 +32,7 @@ from libs.constants import *
from libs.lib import struct, newAction, newIcon, addActions, fmtShortcut, generateColorByText from libs.lib import struct, newAction, newIcon, addActions, fmtShortcut, generateColorByText
from libs.settings import Settings from libs.settings import Settings
from libs.shape import Shape, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR from libs.shape import Shape, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR
from libs.stringBundle import StringBundle
from libs.canvas import Canvas from libs.canvas import Canvas
from libs.zoomWidget import ZoomWidget from libs.zoomWidget import ZoomWidget
from libs.labelDialog import LabelDialog from libs.labelDialog import LabelDialog
@ -44,6 +45,7 @@ from libs.yolo_io import YoloReader
from libs.yolo_io import TXT_EXT from libs.yolo_io import TXT_EXT
from libs.ustr import ustr from libs.ustr import ustr
from libs.version import __version__ from libs.version import __version__
from libs.hashableQListWidgetItem import HashableQListWidgetItem
__appname__ = 'labelImg' __appname__ = 'labelImg'
@ -76,16 +78,6 @@ class WindowMixin(object):
return toolbar return toolbar
# PyQt5: TypeError: unhashable type: 'QListWidgetItem'
class HashableQListWidgetItem(QListWidgetItem):
def __init__(self, *args):
super(HashableQListWidgetItem, self).__init__(*args)
def __hash__(self):
return hash(id(self))
class MainWindow(QMainWindow, WindowMixin): class MainWindow(QMainWindow, WindowMixin):
FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = list(range(3)) FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = list(range(3))
@ -98,6 +90,10 @@ class MainWindow(QMainWindow, WindowMixin):
self.settings.load() self.settings.load()
settings = self.settings settings = self.settings
# Load string bundle for i18n
self.stringBundle = StringBundle.getBundle()
getStr = lambda strId: self.stringBundle.getString(strId)
# Save as Pascal voc xml # Save as Pascal voc xml
self.defaultSaveDir = defaultSaveDir self.defaultSaveDir = defaultSaveDir
self.usingPascalVocFormat = True self.usingPascalVocFormat = True
@ -131,7 +127,7 @@ class MainWindow(QMainWindow, WindowMixin):
listLayout.setContentsMargins(0, 0, 0, 0) listLayout.setContentsMargins(0, 0, 0, 0)
# Create a widget for using default label # Create a widget for using default label
self.useDefaultLabelCheckbox = QCheckBox(u'Use default label') self.useDefaultLabelCheckbox = QCheckBox(getStr('useDefaultLabel'))
self.useDefaultLabelCheckbox.setChecked(False) self.useDefaultLabelCheckbox.setChecked(False)
self.defaultLabelTextLine = QLineEdit() self.defaultLabelTextLine = QLineEdit()
useDefaultLabelQHBoxLayout = QHBoxLayout() useDefaultLabelQHBoxLayout = QHBoxLayout()
@ -141,7 +137,7 @@ class MainWindow(QMainWindow, WindowMixin):
useDefaultLabelContainer.setLayout(useDefaultLabelQHBoxLayout) useDefaultLabelContainer.setLayout(useDefaultLabelQHBoxLayout)
# Create a widget for edit and diffc button # Create a widget for edit and diffc button
self.diffcButton = QCheckBox(u'difficult') self.diffcButton = QCheckBox(getStr('useDifficult'))
self.diffcButton.setChecked(False) self.diffcButton.setChecked(False)
self.diffcButton.stateChanged.connect(self.btnstate) self.diffcButton.stateChanged.connect(self.btnstate)
self.editButton = QToolButton() self.editButton = QToolButton()
@ -163,11 +159,10 @@ class MainWindow(QMainWindow, WindowMixin):
self.labelList.itemChanged.connect(self.labelItemChanged) self.labelList.itemChanged.connect(self.labelItemChanged)
listLayout.addWidget(self.labelList) listLayout.addWidget(self.labelList)
self.dock = QDockWidget(u'Box Labels', self) self.dock = QDockWidget(getStr('boxLabelText'), self)
self.dock.setObjectName(u'Labels') self.dock.setObjectName(getStr('labels'))
self.dock.setWidget(labelListContainer) self.dock.setWidget(labelListContainer)
# Tzutalin 20160906 : Add file list and dock to move faster
self.fileListWidget = QListWidget() self.fileListWidget = QListWidget()
self.fileListWidget.itemDoubleClicked.connect(self.fileitemDoubleClicked) self.fileListWidget.itemDoubleClicked.connect(self.fileitemDoubleClicked)
filelistLayout = QVBoxLayout() filelistLayout = QVBoxLayout()
@ -175,8 +170,8 @@ class MainWindow(QMainWindow, WindowMixin):
filelistLayout.addWidget(self.fileListWidget) filelistLayout.addWidget(self.fileListWidget)
fileListContainer = QWidget() fileListContainer = QWidget()
fileListContainer.setLayout(filelistLayout) fileListContainer.setLayout(filelistLayout)
self.filedock = QDockWidget(u'File List', self) self.filedock = QDockWidget(getStr('fileList'), self)
self.filedock.setObjectName(u'Files') self.filedock.setObjectName(getStr('files'))
self.filedock.setWidget(fileListContainer) self.filedock.setWidget(fileListContainer)
self.zoomWidget = ZoomWidget() self.zoomWidget = ZoomWidget()
@ -203,7 +198,6 @@ class MainWindow(QMainWindow, WindowMixin):
self.setCentralWidget(scroll) self.setCentralWidget(scroll)
self.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.addDockWidget(Qt.RightDockWidgetArea, self.dock)
# Tzutalin 20160906 : Add file list and dock to move faster
self.addDockWidget(Qt.RightDockWidgetArea, self.filedock) self.addDockWidget(Qt.RightDockWidgetArea, self.filedock)
self.filedock.setFeatures(QDockWidget.DockWidgetFloatable) self.filedock.setFeatures(QDockWidget.DockWidgetFloatable)
@ -212,72 +206,72 @@ class MainWindow(QMainWindow, WindowMixin):
# Actions # Actions
action = partial(newAction, self) action = partial(newAction, self)
quit = action('&Quit', self.close, quit = action(getStr('quit'), self.close,
'Ctrl+Q', 'quit', u'Quit application') 'Ctrl+Q', 'quit', getStr('quitApp'))
open = action('&Open', self.openFile, open = action(getStr('openFile'), self.openFile,
'Ctrl+O', 'open', u'Open image or label file') 'Ctrl+O', 'open', getStr('openFileDetail'))
opendir = action('&Open Dir', self.openDirDialog, opendir = action(getStr('openDir'), self.openDirDialog,
'Ctrl+u', 'open', u'Open Dir') 'Ctrl+u', 'open', getStr('openDir'))
changeSavedir = action('&Change Save Dir', self.changeSavedirDialog, changeSavedir = action(getStr('changeSaveDir'), self.changeSavedirDialog,
'Ctrl+r', 'open', u'Change default saved Annotation dir') 'Ctrl+r', 'open', getStr('changeSavedAnnotationDir'))
openAnnotation = action('&Open Annotation', self.openAnnotationDialog, openAnnotation = action(getStr('openAnnotation'), self.openAnnotationDialog,
'Ctrl+Shift+O', 'open', u'Open Annotation') 'Ctrl+Shift+O', 'open', getStr('openAnnotationDetail'))
openNextImg = action('&Next Image', self.openNextImg, openNextImg = action(getStr('nextImg'), self.openNextImg,
'd', 'next', u'Open Next') 'd', 'next', getStr('nextImgDetail'))
openPrevImg = action('&Prev Image', self.openPrevImg, openPrevImg = action(getStr('prevImg'), self.openPrevImg,
'a', 'prev', u'Open Prev') 'a', 'prev', getStr('prevImgDetail'))
verify = action('&Verify Image', self.verifyImg, verify = action(getStr('verifyImg'), self.verifyImg,
'space', 'verify', u'Verify Image') 'space', 'verify', getStr('verifyImgDetail'))
save = action('&Save', self.saveFile, save = action(getStr('save'), self.saveFile,
'Ctrl+S', 'save', u'Save labels to file', enabled=False) 'Ctrl+S', 'save', getStr('saveDetail'), enabled=False)
save_format = action('&PascalVOC', self.change_format, save_format = action('&PascalVOC', self.change_format,
'Ctrl+', 'format_voc', u'Change save format', enabled=True) 'Ctrl+', 'format_voc', getStr('changeSaveFormat'), enabled=True)
saveAs = action('&Save As', self.saveFileAs, saveAs = action(getStr('saveAs'), self.saveFileAs,
'Ctrl+Shift+S', 'save-as', u'Save labels to a different file', enabled=False) 'Ctrl+Shift+S', 'save-as', getStr('saveAsDetail'), enabled=False)
close = action('&Close', self.closeFile, 'Ctrl+W', 'close', u'Close current file') close = action(getStr('closeCur'), self.closeFile, 'Ctrl+W', 'close', getStr('closeCurDetail'))
resetAll = action('&ResetAll', self.resetAll, None, 'resetall', u'Reset all') resetAll = action(getStr('resetAll'), self.resetAll, None, 'resetall', getStr('resetAllDetail'))
color1 = action('Box Line Color', self.chooseColor1, color1 = action(getStr('boxLineColor'), self.chooseColor1,
'Ctrl+L', 'color_line', u'Choose Box line color') 'Ctrl+L', 'color_line', getStr('boxLineColorDetail'))
createMode = action('Create\nRectBox', self.setCreateMode, createMode = action(getStr('crtBox'), self.setCreateMode,
'w', 'new', u'Start drawing Boxs', enabled=False) 'w', 'new', getStr('crtBoxDetail'), enabled=False)
editMode = action('&Edit\nRectBox', self.setEditMode, editMode = action('&Edit\nRectBox', self.setEditMode,
'Ctrl+J', 'edit', u'Move and edit Boxs', enabled=False) 'Ctrl+J', 'edit', u'Move and edit Boxs', enabled=False)
create = action('Create\nRectBox', self.createShape, create = action(getStr('crtBox'), self.createShape,
'w', 'new', u'Draw a new Box', enabled=False) 'w', 'new', getStr('crtBoxDetail'), enabled=False)
delete = action('Delete\nRectBox', self.deleteSelectedShape, delete = action(getStr('delBox'), self.deleteSelectedShape,
'Delete', 'delete', u'Delete', enabled=False) 'Delete', 'delete', getStr('delBoxDetail'), enabled=False)
copy = action('&Duplicate\nRectBox', self.copySelectedShape, copy = action(getStr('dupBox'), self.copySelectedShape,
'Ctrl+D', 'copy', u'Create a duplicate of the selected Box', 'Ctrl+D', 'copy', getStr('dupBoxDetail'),
enabled=False) enabled=False)
advancedMode = action('&Advanced Mode', self.toggleAdvancedMode, advancedMode = action(getStr('advancedMode'), self.toggleAdvancedMode,
'Ctrl+Shift+A', 'expert', u'Switch to advanced mode', 'Ctrl+Shift+A', 'expert', getStr('advancedModeDetail'),
checkable=True) checkable=True)
hideAll = action('&Hide\nRectBox', partial(self.togglePolygons, False), hideAll = action('&Hide\nRectBox', partial(self.togglePolygons, False),
'Ctrl+H', 'hide', u'Hide all Boxs', 'Ctrl+H', 'hide', getStr('hideAllBoxDetail'),
enabled=False) enabled=False)
showAll = action('&Show\nRectBox', partial(self.togglePolygons, True), showAll = action('&Show\nRectBox', partial(self.togglePolygons, True),
'Ctrl+A', 'hide', u'Show all Boxs', 'Ctrl+A', 'hide', getStr('showAllBoxDetail'),
enabled=False) enabled=False)
help = action('&Tutorial', self.showTutorialDialog, None, 'help', u'Show demos') help = action(getStr('tutorial'), self.showTutorialDialog, None, 'help', getStr('tutorialDetail'))
showInfo = action('&Information', self.showInfoDialog, None, 'help', u'Information') showInfo = action(getStr('info'), self.showInfoDialog, None, 'help', getStr('info'))
zoom = QWidgetAction(self) zoom = QWidgetAction(self)
zoom.setDefaultWidget(self.zoomWidget) zoom.setDefaultWidget(self.zoomWidget)
@ -287,17 +281,17 @@ class MainWindow(QMainWindow, WindowMixin):
fmtShortcut("Ctrl+Wheel"))) fmtShortcut("Ctrl+Wheel")))
self.zoomWidget.setEnabled(False) self.zoomWidget.setEnabled(False)
zoomIn = action('Zoom &In', partial(self.addZoom, 10), zoomIn = action(getStr('zoomin'), partial(self.addZoom, 10),
'Ctrl++', 'zoom-in', u'Increase zoom level', enabled=False) 'Ctrl++', 'zoom-in', getStr('zoominDetail'), enabled=False)
zoomOut = action('&Zoom Out', partial(self.addZoom, -10), zoomOut = action(getStr('zoomout'), partial(self.addZoom, -10),
'Ctrl+-', 'zoom-out', u'Decrease zoom level', enabled=False) 'Ctrl+-', 'zoom-out', getStr('zoomoutDetail'), enabled=False)
zoomOrg = action('&Original size', partial(self.setZoom, 100), zoomOrg = action(getStr('originalsize'), partial(self.setZoom, 100),
'Ctrl+=', 'zoom', u'Zoom to original size', enabled=False) 'Ctrl+=', 'zoom', getStr('originalsizeDetail'), enabled=False)
fitWindow = action('&Fit Window', self.setFitWindow, fitWindow = action(getStr('fitWin'), self.setFitWindow,
'Ctrl+F', 'fit-window', u'Zoom follows window size', 'Ctrl+F', 'fit-window', getStr('fitWinDetail'),
checkable=True, enabled=False) checkable=True, enabled=False)
fitWidth = action('Fit &Width', self.setFitWidth, fitWidth = action(getStr('fitWidth'), self.setFitWidth,
'Ctrl+Shift+F', 'fit-width', u'Zoom follows window width', 'Ctrl+Shift+F', 'fit-width', getStr('fitWidthDetail'),
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, zoomActions = (self.zoomWidget, zoomIn, zoomOut,
@ -310,20 +304,20 @@ class MainWindow(QMainWindow, WindowMixin):
self.MANUAL_ZOOM: lambda: 1, self.MANUAL_ZOOM: lambda: 1,
} }
edit = action('&Edit Label', self.editLabel, edit = action(getStr('editLabel'), self.editLabel,
'Ctrl+E', 'edit', u'Modify the label of the selected Box', 'Ctrl+E', 'edit', getStr('editLabelDetail'),
enabled=False) enabled=False)
self.editButton.setDefaultAction(edit) self.editButton.setDefaultAction(edit)
shapeLineColor = action('Shape &Line Color', self.chshapeLineColor, shapeLineColor = action(getStr('shapeLineColor'), self.chshapeLineColor,
icon='color_line', tip=u'Change the line color for this specific shape', icon='color_line', tip=getStr('shapeLineColorDetail'),
enabled=False) enabled=False)
shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor, shapeFillColor = action(getStr('shapeFillColor'), self.chshapeFillColor,
icon='color', tip=u'Change the fill color for this specific shape', icon='color', tip=getStr('shapeFillColorDetail'),
enabled=False) enabled=False)
labels = self.dock.toggleViewAction() labels = self.dock.toggleViewAction()
labels.setText('Show/Hide Label Panel') labels.setText(getStr('showHide'))
labels.setShortcut('Ctrl+Shift+L') labels.setShortcut('Ctrl+Shift+L')
# Lavel list context menu. # Lavel list context menu.
@ -369,21 +363,21 @@ class MainWindow(QMainWindow, WindowMixin):
labelList=labelMenu) labelList=labelMenu)
# Auto saving : Enable auto saving if pressing next # Auto saving : Enable auto saving if pressing next
self.autoSaving = QAction("Auto Saving", self) self.autoSaving = QAction(getStr('autoSaveMode'), self)
self.autoSaving.setCheckable(True) self.autoSaving.setCheckable(True)
self.autoSaving.setChecked(settings.get(SETTING_AUTO_SAVE, False)) self.autoSaving.setChecked(settings.get(SETTING_AUTO_SAVE, False))
# Sync single class mode from PR#106 # Sync single class mode from PR#106
self.singleClassMode = QAction("Single Class Mode", self) self.singleClassMode = QAction(getStr('singleClsMode'), self)
self.singleClassMode.setShortcut("Ctrl+Shift+S") self.singleClassMode.setShortcut("Ctrl+Shift+S")
self.singleClassMode.setCheckable(True) self.singleClassMode.setCheckable(True)
self.singleClassMode.setChecked(settings.get(SETTING_SINGLE_CLASS, False)) self.singleClassMode.setChecked(settings.get(SETTING_SINGLE_CLASS, False))
self.lastLabel = None self.lastLabel = None
# Add option to enable/disable labels being painted at the top of bounding boxes # Add option to enable/disable labels being displayed at the top of bounding boxes
self.paintLabelsOption = QAction("Paint Labels", self) self.displayLabelOption = QAction(getStr('displayLabel'), self)
self.paintLabelsOption.setShortcut("Ctrl+Shift+P") self.displayLabelOption.setShortcut("Ctrl+Shift+P")
self.paintLabelsOption.setCheckable(True) self.displayLabelOption.setCheckable(True)
self.paintLabelsOption.setChecked(settings.get(SETTING_PAINT_LABEL, False)) self.displayLabelOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
self.paintLabelsOption.triggered.connect(self.togglePaintLabelsOption) self.displayLabelOption.triggered.connect(self.togglePaintLabelsOption)
addActions(self.menus.file, addActions(self.menus.file,
(open, opendir, changeSavedir, openAnnotation, self.menus.recentFiles, save, save_format, saveAs, close, resetAll, quit)) (open, opendir, changeSavedir, openAnnotation, self.menus.recentFiles, save, save_format, saveAs, close, resetAll, quit))
@ -391,7 +385,7 @@ class MainWindow(QMainWindow, WindowMixin):
addActions(self.menus.view, ( addActions(self.menus.view, (
self.autoSaving, self.autoSaving,
self.singleClassMode, self.singleClassMode,
self.paintLabelsOption, self.displayLabelOption,
labels, advancedMode, None, labels, advancedMode, None,
hideAll, showAll, None, hideAll, showAll, None,
zoomIn, zoomOut, zoomOrg, None, zoomIn, zoomOut, zoomOrg, None,
@ -739,7 +733,7 @@ class MainWindow(QMainWindow, WindowMixin):
self.actions.shapeFillColor.setEnabled(selected) self.actions.shapeFillColor.setEnabled(selected)
def addLabel(self, shape): def addLabel(self, shape):
shape.paintLabel = self.paintLabelsOption.isChecked() shape.paintLabel = self.displayLabelOption.isChecked()
item = HashableQListWidgetItem(shape.label) item = HashableQListWidgetItem(shape.label)
item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Checked) item.setCheckState(Qt.Checked)
@ -1112,19 +1106,18 @@ class MainWindow(QMainWindow, WindowMixin):
if self.defaultSaveDir and os.path.exists(self.defaultSaveDir): if self.defaultSaveDir and os.path.exists(self.defaultSaveDir):
settings[SETTING_SAVE_DIR] = ustr(self.defaultSaveDir) settings[SETTING_SAVE_DIR] = ustr(self.defaultSaveDir)
else: else:
settings[SETTING_SAVE_DIR] = "" settings[SETTING_SAVE_DIR] = ''
if self.lastOpenDir and os.path.exists(self.lastOpenDir): if self.lastOpenDir and os.path.exists(self.lastOpenDir):
settings[SETTING_LAST_OPEN_DIR] = self.lastOpenDir settings[SETTING_LAST_OPEN_DIR] = self.lastOpenDir
else: else:
settings[SETTING_LAST_OPEN_DIR] = "" settings[SETTING_LAST_OPEN_DIR] = ''
settings[SETTING_AUTO_SAVE] = self.autoSaving.isChecked() settings[SETTING_AUTO_SAVE] = self.autoSaving.isChecked()
settings[SETTING_SINGLE_CLASS] = self.singleClassMode.isChecked() settings[SETTING_SINGLE_CLASS] = self.singleClassMode.isChecked()
settings[SETTING_PAINT_LABEL] = self.paintLabelsOption.isChecked() settings[SETTING_PAINT_LABEL] = self.displayLabelOption.isChecked()
settings[SETTING_DRAW_SQUARE] = self.drawSquaresOption.isChecked() settings[SETTING_DRAW_SQUARE] = self.drawSquaresOption.isChecked()
settings.save() settings.save()
## User Dialogs ##
def loadRecent(self, filename): def loadRecent(self, filename):
if self.mayContinue(): if self.mayContinue():
@ -1195,7 +1188,6 @@ class MainWindow(QMainWindow, WindowMixin):
if not self.mayContinue() or not dirpath: if not self.mayContinue() or not dirpath:
return return
self.lastOpenDir = dirpath self.lastOpenDir = dirpath
self.dirname = dirpath self.dirname = dirpath
self.filePath = None self.filePath = None
@ -1437,9 +1429,8 @@ class MainWindow(QMainWindow, WindowMixin):
self.canvas.verified = tYoloParseReader.verified self.canvas.verified = tYoloParseReader.verified
def togglePaintLabelsOption(self): def togglePaintLabelsOption(self):
paintLabelsOptionChecked = self.paintLabelsOption.isChecked()
for shape in self.canvas.shapes: for shape in self.canvas.shapes:
shape.paintLabel = paintLabelsOptionChecked shape.paintLabel = self.displayLabelOption.isChecked()
def toogleDrawSquare(self): def toogleDrawSquare(self):
self.canvas.setDrawingShapeToSquare(self.drawSquaresOption.isChecked()) self.canvas.setDrawingShapeToSquare(self.drawSquaresOption.isChecked())

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
try:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
except ImportError:
# needed for py3+qt4
# Ref:
# http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html
# http://stackoverflow.com/questions/21217399/pyqt4-qtcore-qvariant-object-instead-of-a-string
if sys.version_info.major >= 3:
import sip
sip.setapi('QVariant', 2)
from PyQt4.QtGui import *
from PyQt4.QtCore import *
# PyQt5: TypeError: unhashable type: 'QListWidgetItem'
class HashableQListWidgetItem(QListWidgetItem):
def __init__(self, *args):
super(HashableQListWidgetItem, self).__init__(*args)
def __hash__(self):
return hash(id(self))

View File

@ -75,7 +75,7 @@ def fmtShortcut(text):
def generateColorByText(text): def generateColorByText(text):
s = str(ustr(text)) s = ustr(text)
hashCode = int(hashlib.sha256(s.encode('utf-8')).hexdigest(), 16) hashCode = int(hashlib.sha256(s.encode('utf-8')).hexdigest(), 16)
r = int((hashCode / 255) % 255) r = int((hashCode / 255) % 255)
g = int((hashCode / 65025) % 255) g = int((hashCode / 65025) % 255)

63
libs/stringBundle.py Normal file
View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
import resources
import os
import sys
from libs.ustr import ustr
try:
from PyQt5.QtCore import *
except ImportError:
if sys.version_info.major >= 3:
import sip
sip.setapi('QVariant', 2)
from PyQt4.QtCore import *
class StringBundle:
__create_key = object()
def __init__(self, create_key, localeStr):
assert(create_key == StringBundle.__create_key), "StringBundle must be created using StringBundle.getBundle"
self.idToMessage = {}
paths = self.__createLookupFallbackList(localeStr)
for path in paths:
self.__loadBundle(path)
@classmethod
def getBundle(cls, localeStr=os.getenv('LANG')):
return StringBundle(cls.__create_key, localeStr)
def getString(self, stringId):
assert(stringId in self.idToMessage), "Missing string id : " + stringId
return self.idToMessage[stringId]
def __createLookupFallbackList(self, localeStr):
resultPaths = []
basePath = ":/strings"
resultPaths.append(basePath)
# Don't follow standard BCP47. Simple fallback
tags = re.split('[^a-zA-Z]', localeStr)
for tag in tags:
lastPath = resultPaths[-1]
resultPaths.append(lastPath + '-' + tag)
return resultPaths
def __loadBundle(self, path):
PROP_SEPERATOR = '='
f = QFile(path)
if f.exists():
if f.open(QIODevice.ReadOnly | QFile.Text):
text = QTextStream(f)
text.setCodec("UTF-8")
while not text.atEnd():
line = ustr(text.readLine())
key_value = line.split(PROP_SEPERATOR)
key = key_value[0].strip()
value = PROP_SEPERATOR.join(key_value[1:]).strip().strip('"')
self.idToMessage[key] = value
f.close()

View File

@ -9,7 +9,7 @@ def ustr(x):
if type(x) == str: if type(x) == str:
return x.decode(DEFAULT_ENCODING) return x.decode(DEFAULT_ENCODING)
if type(x) == QString: if type(x) == QString:
return unicode(x, DEFAULT_ENCODING) return unicode(x)
return x return x
else: else:
return x return x

View File

@ -1,35 +1,37 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource> <qresource>
<file alias="help">icons/help.png</file> <file alias="help">resources/icons/help.png</file>
<file alias="app">icons/app.png</file> <file alias="app">resources/icons/app.png</file>
<file alias="expert">icons/expert2.png</file> <file alias="expert">resources/icons/expert2.png</file>
<file alias="done">icons/done.png</file> <file alias="done">resources/icons/done.png</file>
<file alias="file">icons/file.png</file> <file alias="file">resources/icons/file.png</file>
<file alias="labels">icons/labels.png</file> <file alias="labels">resources/icons/labels.png</file>
<file alias="new">icons/objects.png</file> <file alias="new">resources/icons/objects.png</file>
<file alias="close">icons/close.png</file> <file alias="close">resources/icons/close.png</file>
<file alias="fit-width">icons/fit-width.png</file> <file alias="fit-width">resources/icons/fit-width.png</file>
<file alias="fit-window">icons/fit-window.png</file> <file alias="fit-window">resources/icons/fit-window.png</file>
<file alias="undo">icons/undo.png</file> <file alias="undo">resources/icons/undo.png</file>
<file alias="hide">icons/eye.png</file> <file alias="hide">resources/icons/eye.png</file>
<file alias="quit">icons/quit.png</file> <file alias="quit">resources/icons/quit.png</file>
<file alias="copy">icons/copy.png</file> <file alias="copy">resources/icons/copy.png</file>
<file alias="edit">icons/edit.png</file> <file alias="edit">resources/icons/edit.png</file>
<file alias="open">icons/open.png</file> <file alias="open">resources/icons/open.png</file>
<file alias="save">icons/save.png</file> <file alias="save">resources/icons/save.png</file>
<file alias="format_voc">icons/format_voc.png</file> <file alias="format_voc">resources/icons/format_voc.png</file>
<file alias="format_yolo">icons/format_yolo.png</file> <file alias="format_yolo">resources/icons/format_yolo.png</file>
<file alias="save-as">icons/save-as.png</file> <file alias="save-as">resources/icons/save-as.png</file>
<file alias="color">icons/color.png</file> <file alias="color">resources/icons/color.png</file>
<file alias="color_line">icons/color_line.png</file> <file alias="color_line">resources/icons/color_line.png</file>
<file alias="zoom">icons/zoom.png</file> <file alias="zoom">resources/icons/zoom.png</file>
<file alias="zoom-in">icons/zoom-in.png</file> <file alias="zoom-in">resources/icons/zoom-in.png</file>
<file alias="zoom-out">icons/zoom-out.png</file> <file alias="zoom-out">resources/icons/zoom-out.png</file>
<file alias="delete">icons/cancel.png</file> <file alias="delete">resources/icons/cancel.png</file>
<file alias="next">icons/next.png</file> <file alias="next">resources/icons/next.png</file>
<file alias="prev">icons/prev.png</file> <file alias="prev">resources/icons/prev.png</file>
<file alias="resetall">icons/resetall.png</file> <file alias="resetall">resources/icons/resetall.png</file>
<file alias="verify">icons/verify.png</file> <file alias="verify">resources/icons/verify.png</file>
<file alias="strings">resources/strings/strings.properties</file>
<file alias="strings-zh-TW">resources/strings/strings-zh-TW.properties</file>
</qresource> </qresource>
</RCC> </RCC>

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 278 B

View File

Before

Width:  |  Height:  |  Size: 335 B

After

Width:  |  Height:  |  Size: 335 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

Before

Width:  |  Height:  |  Size: 765 B

After

Width:  |  Height:  |  Size: 765 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 786 B

After

Width:  |  Height:  |  Size: 786 B

View File

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 675 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 977 B

After

Width:  |  Height:  |  Size: 977 B

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,65 @@
saveAsDetail=將標籤保存到其他文件
changeSaveDir=改變存放目錄
openFile=開啟檔案
shapeLineColorDetail=更改線條顏色
resetAll=重置
crtBox=創建區塊
crtBoxDetail=畫一個區塊
dupBoxDetail=複製區塊
verifyImg=驗證圖像
zoominDetail=放大
verifyImgDetail=驗證圖像
saveDetail=將標籤存到
openFileDetail=打開圖像
fitWidthDetail=調整到窗口寬度
tutorial=YouTube教學
editLabel=編輯標籤
openAnnotationDetail=打開標籤文件
quit=結束
shapeFillColorDetail=更改填充顏色
closeCurDetail=關閉目前檔案
closeCur=關閉
fitWin=調整到跟窗口一樣大小
delBox=刪除選取區塊
boxLineColorDetail=選擇框線顏色
originalsize=原始大小
resetAllDetail=重設所有設定
zoomoutDetail=畫面放大
save=儲存
saveAs=另存為
fitWinDetail=縮放到窗口一樣
openDir=開啟目錄
showHide=顯示/隱藏標籤
changeSaveFormat=更改儲存格式
shapeFillColor=填充顏色
quitApp=離開本程式
dupBox=複製區塊
delBoxDetail=刪除區塊
zoomin=放大畫面
info=資訊
openAnnotation=開啟標籤
prevImgDetail=上一個圖像
fitWidth=縮放到跟畫面一樣寬
zoomout=縮小畫面
changeSavedAnnotationDir=更改預設標籤存的目錄
nextImgDetail=下一個圖像
originalsizeDetail=放大到原始大小
prevImg=上一個圖像
tutorialDetail=顯示示範內容
shapeLineColor=形狀線條顏色
boxLineColor=日期分隔線顏色
editLabelDetail=修改所選區塊的標籤
nextImg=下一張圖片
useDefaultLabel=使用預設標籤
useDifficult=有難度的
boxLabelText=區塊的標籤
labels=標籤
autoSaveMode=自動儲存模式
singleClsMode=單一類別模式
displayLabel=顯示類別
fileList=檔案清單
files=檔案
advancedMode=進階模式
advancedModeDetail=切到進階模式
showAllBoxDetail=顯示所有區塊
hideAllBoxDetail=隱藏所有區塊

View File

@ -0,0 +1,65 @@
openFile=Open
openFileDetail=Open image or label file
quit=Quit
quitApp=Quit application
openDir=Open Dir
changeSavedAnnotationDir=Change default saved Annotation dir
openAnnotation=Open Annotation
openAnnotationDetail=Open an annotation file
changeSaveDir=Change Save Dir
nextImg=Next Image
nextImgDetail=Open the next Image
prevImg=Prev Image
prevImgDetail=Open the previous Image
verifyImg=Verify Image
verifyImgDetail=Verify Image
save=Save
saveDetail=Save the labels to a file
changeSaveFormat=Change save format
saveAs=Save As
saveAsDetail=Save the labels to a different file
closeCur=Close
closeCurDetail=Close the current file
resetAll=Reset All
resetAllDetail=Reset All
boxLineColor=Box Line Color
boxLineColorDetail=Choose Box line color
crtBox=Create\nRectBox
crtBoxDetail=Draw a new box
delBox=Delete\nRectBox
delBoxDetail=Remove the box
dupBox=Duplicate\nRectBox
dupBoxDetail=Create a duplicate of the selected box
tutorial=Tutorial
tutorialDetail=Show demo
info=Information
zoomin=Zoom In
zoominDetail=Increase zoom level
zoomout=Zoom Out
zoomoutDetail=Decrease zoom level
originalsize=Original size
originalsizeDetail=Zoom to original size
fitWin=Fit Window
fitWinDetail=Zoom follows window size
fitWidth=Fit Widith
fitWidthDetail=Zoom follows window width
editLabel=Edit Label
editLabelDetail=Modify the label of the selected Box
shapeLineColor=Shape Line Color
shapeLineColorDetail=Change the line color for this specific shape
shapeFillColor=Shape Fill Color
shapeFillColorDetail=Change the fill color for this specific shape
showHide=Show/Hide Label Panel
useDefaultLabel=Use default labtel
useDifficult=difficult
boxLabelText=Box Labels
labels=Labels
autoSaveMode=Auto Save mode
singleClsMode=Single Class Mode
displayLabel=Display Labels
fileList=File List
files=Files
advancedMode=Advanced Mode
advancedModeDetail=Swtich to advanced mode
showAllBoxDetail=Show all bounding boxes
hideAllBoxDetail=Hide all bounding boxes

View File

@ -30,7 +30,7 @@ required_packages.append('labelImg')
APP = ['labelImg.py'] APP = ['labelImg.py']
OPTIONS = { OPTIONS = {
'argv_emulation': True, 'argv_emulation': True,
'iconfile': 'icons/app.icns' 'iconfile': 'resources/icons/app.icns'
} }
setup( setup(

15
tests/test_lib.py Normal file
View File

@ -0,0 +1,15 @@
import os
import sys
import unittest
from libs.lib import struct, newAction, newIcon, addActions, fmtShortcut, generateColorByText
class TestLib(unittest.TestCase):
def test_generateColorByGivingUniceText_noError(self):
res = generateColorByText(u'\u958B\u555F\u76EE\u9304')
self.assertTrue(res.green() >= 0)
self.assertTrue(res.red() >= 0)
self.assertTrue(res.blue() >= 0)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,17 @@
import os
import sys
import unittest
from stringBundle import StringBundle
class TestStringBundle(unittest.TestCase):
def test_loadDefaultBundle_withoutError(self):
strBundle = StringBundle.getBundle('en')
self.assertEqual(strBundle.getString("openDir"), 'Open Dir', 'Fail to load the default bundle')
def test_fallback_withoutError(self):
strBundle = StringBundle.getBundle('zh-TW')
self.assertEqual(strBundle.getString("openDir"), u'\u958B\u555F\u76EE\u9304', 'Fail to load the zh-TW bundle')
if __name__ == '__main__':
unittest.main()