Move icons to resource folder, fix the unicode issue, support zh-tw lang
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
icons/.DS_Store
|
||||
resources/icons/.DS_Store
|
||||
|
||||
resources.py
|
||||
|
||||
|
||||
173
labelImg.py
@ -32,6 +32,7 @@ from libs.constants import *
|
||||
from libs.lib import struct, newAction, newIcon, addActions, fmtShortcut, generateColorByText
|
||||
from libs.settings import Settings
|
||||
from libs.shape import Shape, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR
|
||||
from libs.stringBundle import StringBundle
|
||||
from libs.canvas import Canvas
|
||||
from libs.zoomWidget import ZoomWidget
|
||||
from libs.labelDialog import LabelDialog
|
||||
@ -44,6 +45,7 @@ from libs.yolo_io import YoloReader
|
||||
from libs.yolo_io import TXT_EXT
|
||||
from libs.ustr import ustr
|
||||
from libs.version import __version__
|
||||
from libs.hashableQListWidgetItem import HashableQListWidgetItem
|
||||
|
||||
__appname__ = 'labelImg'
|
||||
|
||||
@ -76,16 +78,6 @@ class WindowMixin(object):
|
||||
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):
|
||||
FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = list(range(3))
|
||||
|
||||
@ -98,6 +90,10 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
self.settings.load()
|
||||
settings = self.settings
|
||||
|
||||
# Load string bundle for i18n
|
||||
self.stringBundle = StringBundle.getBundle()
|
||||
getStr = lambda strId: self.stringBundle.getString(strId)
|
||||
|
||||
# Save as Pascal voc xml
|
||||
self.defaultSaveDir = defaultSaveDir
|
||||
self.usingPascalVocFormat = True
|
||||
@ -131,7 +127,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
listLayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Create a widget for using default label
|
||||
self.useDefaultLabelCheckbox = QCheckBox(u'Use default label')
|
||||
self.useDefaultLabelCheckbox = QCheckBox(getStr('useDefaultLabel'))
|
||||
self.useDefaultLabelCheckbox.setChecked(False)
|
||||
self.defaultLabelTextLine = QLineEdit()
|
||||
useDefaultLabelQHBoxLayout = QHBoxLayout()
|
||||
@ -141,7 +137,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
useDefaultLabelContainer.setLayout(useDefaultLabelQHBoxLayout)
|
||||
|
||||
# Create a widget for edit and diffc button
|
||||
self.diffcButton = QCheckBox(u'difficult')
|
||||
self.diffcButton = QCheckBox(getStr('useDifficult'))
|
||||
self.diffcButton.setChecked(False)
|
||||
self.diffcButton.stateChanged.connect(self.btnstate)
|
||||
self.editButton = QToolButton()
|
||||
@ -163,11 +159,10 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
self.labelList.itemChanged.connect(self.labelItemChanged)
|
||||
listLayout.addWidget(self.labelList)
|
||||
|
||||
self.dock = QDockWidget(u'Box Labels', self)
|
||||
self.dock.setObjectName(u'Labels')
|
||||
self.dock = QDockWidget(getStr('boxLabelText'), self)
|
||||
self.dock.setObjectName(getStr('labels'))
|
||||
self.dock.setWidget(labelListContainer)
|
||||
|
||||
# Tzutalin 20160906 : Add file list and dock to move faster
|
||||
self.fileListWidget = QListWidget()
|
||||
self.fileListWidget.itemDoubleClicked.connect(self.fileitemDoubleClicked)
|
||||
filelistLayout = QVBoxLayout()
|
||||
@ -175,8 +170,8 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
filelistLayout.addWidget(self.fileListWidget)
|
||||
fileListContainer = QWidget()
|
||||
fileListContainer.setLayout(filelistLayout)
|
||||
self.filedock = QDockWidget(u'File List', self)
|
||||
self.filedock.setObjectName(u'Files')
|
||||
self.filedock = QDockWidget(getStr('fileList'), self)
|
||||
self.filedock.setObjectName(getStr('files'))
|
||||
self.filedock.setWidget(fileListContainer)
|
||||
|
||||
self.zoomWidget = ZoomWidget()
|
||||
@ -203,7 +198,6 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
|
||||
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.filedock.setFeatures(QDockWidget.DockWidgetFloatable)
|
||||
|
||||
@ -212,72 +206,72 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
|
||||
# Actions
|
||||
action = partial(newAction, self)
|
||||
quit = action('&Quit', self.close,
|
||||
'Ctrl+Q', 'quit', u'Quit application')
|
||||
quit = action(getStr('quit'), self.close,
|
||||
'Ctrl+Q', 'quit', getStr('quitApp'))
|
||||
|
||||
open = action('&Open', self.openFile,
|
||||
'Ctrl+O', 'open', u'Open image or label file')
|
||||
open = action(getStr('openFile'), self.openFile,
|
||||
'Ctrl+O', 'open', getStr('openFileDetail'))
|
||||
|
||||
opendir = action('&Open Dir', self.openDirDialog,
|
||||
'Ctrl+u', 'open', u'Open Dir')
|
||||
opendir = action(getStr('openDir'), self.openDirDialog,
|
||||
'Ctrl+u', 'open', getStr('openDir'))
|
||||
|
||||
changeSavedir = action('&Change Save Dir', self.changeSavedirDialog,
|
||||
'Ctrl+r', 'open', u'Change default saved Annotation dir')
|
||||
changeSavedir = action(getStr('changeSaveDir'), self.changeSavedirDialog,
|
||||
'Ctrl+r', 'open', getStr('changeSavedAnnotationDir'))
|
||||
|
||||
openAnnotation = action('&Open Annotation', self.openAnnotationDialog,
|
||||
'Ctrl+Shift+O', 'open', u'Open Annotation')
|
||||
openAnnotation = action(getStr('openAnnotation'), self.openAnnotationDialog,
|
||||
'Ctrl+Shift+O', 'open', getStr('openAnnotationDetail'))
|
||||
|
||||
openNextImg = action('&Next Image', self.openNextImg,
|
||||
'd', 'next', u'Open Next')
|
||||
openNextImg = action(getStr('nextImg'), self.openNextImg,
|
||||
'd', 'next', getStr('nextImgDetail'))
|
||||
|
||||
openPrevImg = action('&Prev Image', self.openPrevImg,
|
||||
'a', 'prev', u'Open Prev')
|
||||
openPrevImg = action(getStr('prevImg'), self.openPrevImg,
|
||||
'a', 'prev', getStr('prevImgDetail'))
|
||||
|
||||
verify = action('&Verify Image', self.verifyImg,
|
||||
'space', 'verify', u'Verify Image')
|
||||
verify = action(getStr('verifyImg'), self.verifyImg,
|
||||
'space', 'verify', getStr('verifyImgDetail'))
|
||||
|
||||
save = action('&Save', self.saveFile,
|
||||
'Ctrl+S', 'save', u'Save labels to file', enabled=False)
|
||||
save = action(getStr('save'), self.saveFile,
|
||||
'Ctrl+S', 'save', getStr('saveDetail'), enabled=False)
|
||||
|
||||
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,
|
||||
'Ctrl+Shift+S', 'save-as', u'Save labels to a different file', enabled=False)
|
||||
saveAs = action(getStr('saveAs'), self.saveFileAs,
|
||||
'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,
|
||||
'Ctrl+L', 'color_line', u'Choose Box line color')
|
||||
color1 = action(getStr('boxLineColor'), self.chooseColor1,
|
||||
'Ctrl+L', 'color_line', getStr('boxLineColorDetail'))
|
||||
|
||||
createMode = action('Create\nRectBox', self.setCreateMode,
|
||||
'w', 'new', u'Start drawing Boxs', enabled=False)
|
||||
createMode = action(getStr('crtBox'), self.setCreateMode,
|
||||
'w', 'new', getStr('crtBoxDetail'), 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',
|
||||
create = action(getStr('crtBox'), self.createShape,
|
||||
'w', 'new', getStr('crtBoxDetail'), enabled=False)
|
||||
delete = action(getStr('delBox'), self.deleteSelectedShape,
|
||||
'Delete', 'delete', getStr('delBoxDetail'), enabled=False)
|
||||
copy = action(getStr('dupBox'), self.copySelectedShape,
|
||||
'Ctrl+D', 'copy', getStr('dupBoxDetail'),
|
||||
enabled=False)
|
||||
|
||||
advancedMode = action('&Advanced Mode', self.toggleAdvancedMode,
|
||||
'Ctrl+Shift+A', 'expert', u'Switch to advanced mode',
|
||||
advancedMode = action(getStr('advancedMode'), self.toggleAdvancedMode,
|
||||
'Ctrl+Shift+A', 'expert', getStr('advancedModeDetail'),
|
||||
checkable=True)
|
||||
|
||||
hideAll = action('&Hide\nRectBox', partial(self.togglePolygons, False),
|
||||
'Ctrl+H', 'hide', u'Hide all Boxs',
|
||||
'Ctrl+H', 'hide', getStr('hideAllBoxDetail'),
|
||||
enabled=False)
|
||||
showAll = action('&Show\nRectBox', partial(self.togglePolygons, True),
|
||||
'Ctrl+A', 'hide', u'Show all Boxs',
|
||||
'Ctrl+A', 'hide', getStr('showAllBoxDetail'),
|
||||
enabled=False)
|
||||
|
||||
help = action('&Tutorial', self.showTutorialDialog, None, 'help', u'Show demos')
|
||||
showInfo = action('&Information', self.showInfoDialog, None, 'help', u'Information')
|
||||
help = action(getStr('tutorial'), self.showTutorialDialog, None, 'help', getStr('tutorialDetail'))
|
||||
showInfo = action(getStr('info'), self.showInfoDialog, None, 'help', getStr('info'))
|
||||
|
||||
zoom = QWidgetAction(self)
|
||||
zoom.setDefaultWidget(self.zoomWidget)
|
||||
@ -287,17 +281,17 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
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',
|
||||
zoomIn = action(getStr('zoomin'), partial(self.addZoom, 10),
|
||||
'Ctrl++', 'zoom-in', getStr('zoominDetail'), enabled=False)
|
||||
zoomOut = action(getStr('zoomout'), partial(self.addZoom, -10),
|
||||
'Ctrl+-', 'zoom-out', getStr('zoomoutDetail'), enabled=False)
|
||||
zoomOrg = action(getStr('originalsize'), partial(self.setZoom, 100),
|
||||
'Ctrl+=', 'zoom', getStr('originalsizeDetail'), enabled=False)
|
||||
fitWindow = action(getStr('fitWin'), self.setFitWindow,
|
||||
'Ctrl+F', 'fit-window', getStr('fitWinDetail'),
|
||||
checkable=True, enabled=False)
|
||||
fitWidth = action('Fit &Width', self.setFitWidth,
|
||||
'Ctrl+Shift+F', 'fit-width', u'Zoom follows window width',
|
||||
fitWidth = action(getStr('fitWidth'), self.setFitWidth,
|
||||
'Ctrl+Shift+F', 'fit-width', getStr('fitWidthDetail'),
|
||||
checkable=True, enabled=False)
|
||||
# Group zoom controls into a list for easier toggling.
|
||||
zoomActions = (self.zoomWidget, zoomIn, zoomOut,
|
||||
@ -310,20 +304,20 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
self.MANUAL_ZOOM: lambda: 1,
|
||||
}
|
||||
|
||||
edit = action('&Edit Label', self.editLabel,
|
||||
'Ctrl+E', 'edit', u'Modify the label of the selected Box',
|
||||
edit = action(getStr('editLabel'), self.editLabel,
|
||||
'Ctrl+E', 'edit', getStr('editLabelDetail'),
|
||||
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',
|
||||
shapeLineColor = action(getStr('shapeLineColor'), self.chshapeLineColor,
|
||||
icon='color_line', tip=getStr('shapeLineColorDetail'),
|
||||
enabled=False)
|
||||
shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor,
|
||||
icon='color', tip=u'Change the fill color for this specific shape',
|
||||
shapeFillColor = action(getStr('shapeFillColor'), self.chshapeFillColor,
|
||||
icon='color', tip=getStr('shapeFillColorDetail'),
|
||||
enabled=False)
|
||||
|
||||
labels = self.dock.toggleViewAction()
|
||||
labels.setText('Show/Hide Label Panel')
|
||||
labels.setText(getStr('showHide'))
|
||||
labels.setShortcut('Ctrl+Shift+L')
|
||||
|
||||
# Lavel list context menu.
|
||||
@ -369,21 +363,21 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
labelList=labelMenu)
|
||||
|
||||
# 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.setChecked(settings.get(SETTING_AUTO_SAVE, False))
|
||||
# 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.setCheckable(True)
|
||||
self.singleClassMode.setChecked(settings.get(SETTING_SINGLE_CLASS, False))
|
||||
self.lastLabel = None
|
||||
# Add option to enable/disable labels being painted at the top of bounding boxes
|
||||
self.paintLabelsOption = QAction("Paint Labels", self)
|
||||
self.paintLabelsOption.setShortcut("Ctrl+Shift+P")
|
||||
self.paintLabelsOption.setCheckable(True)
|
||||
self.paintLabelsOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
|
||||
self.paintLabelsOption.triggered.connect(self.togglePaintLabelsOption)
|
||||
# Add option to enable/disable labels being displayed at the top of bounding boxes
|
||||
self.displayLabelOption = QAction(getStr('displayLabel'), self)
|
||||
self.displayLabelOption.setShortcut("Ctrl+Shift+P")
|
||||
self.displayLabelOption.setCheckable(True)
|
||||
self.displayLabelOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
|
||||
self.displayLabelOption.triggered.connect(self.togglePaintLabelsOption)
|
||||
|
||||
addActions(self.menus.file,
|
||||
(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, (
|
||||
self.autoSaving,
|
||||
self.singleClassMode,
|
||||
self.paintLabelsOption,
|
||||
self.displayLabelOption,
|
||||
labels, advancedMode, None,
|
||||
hideAll, showAll, None,
|
||||
zoomIn, zoomOut, zoomOrg, None,
|
||||
@ -739,7 +733,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
self.actions.shapeFillColor.setEnabled(selected)
|
||||
|
||||
def addLabel(self, shape):
|
||||
shape.paintLabel = self.paintLabelsOption.isChecked()
|
||||
shape.paintLabel = self.displayLabelOption.isChecked()
|
||||
item = HashableQListWidgetItem(shape.label)
|
||||
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||
item.setCheckState(Qt.Checked)
|
||||
@ -1112,19 +1106,18 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
if self.defaultSaveDir and os.path.exists(self.defaultSaveDir):
|
||||
settings[SETTING_SAVE_DIR] = ustr(self.defaultSaveDir)
|
||||
else:
|
||||
settings[SETTING_SAVE_DIR] = ""
|
||||
settings[SETTING_SAVE_DIR] = ''
|
||||
|
||||
if self.lastOpenDir and os.path.exists(self.lastOpenDir):
|
||||
settings[SETTING_LAST_OPEN_DIR] = self.lastOpenDir
|
||||
else:
|
||||
settings[SETTING_LAST_OPEN_DIR] = ""
|
||||
settings[SETTING_LAST_OPEN_DIR] = ''
|
||||
|
||||
settings[SETTING_AUTO_SAVE] = self.autoSaving.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.save()
|
||||
## User Dialogs ##
|
||||
|
||||
def loadRecent(self, filename):
|
||||
if self.mayContinue():
|
||||
@ -1195,7 +1188,6 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
if not self.mayContinue() or not dirpath:
|
||||
return
|
||||
|
||||
|
||||
self.lastOpenDir = dirpath
|
||||
self.dirname = dirpath
|
||||
self.filePath = None
|
||||
@ -1437,9 +1429,8 @@ class MainWindow(QMainWindow, WindowMixin):
|
||||
self.canvas.verified = tYoloParseReader.verified
|
||||
|
||||
def togglePaintLabelsOption(self):
|
||||
paintLabelsOptionChecked = self.paintLabelsOption.isChecked()
|
||||
for shape in self.canvas.shapes:
|
||||
shape.paintLabel = paintLabelsOptionChecked
|
||||
shape.paintLabel = self.displayLabelOption.isChecked()
|
||||
|
||||
def toogleDrawSquare(self):
|
||||
self.canvas.setDrawingShapeToSquare(self.drawSquaresOption.isChecked())
|
||||
|
||||
28
libs/hashableQListWidgetItem.py
Normal 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))
|
||||
@ -75,7 +75,7 @@ def fmtShortcut(text):
|
||||
|
||||
|
||||
def generateColorByText(text):
|
||||
s = str(ustr(text))
|
||||
s = ustr(text)
|
||||
hashCode = int(hashlib.sha256(s.encode('utf-8')).hexdigest(), 16)
|
||||
r = int((hashCode / 255) % 255)
|
||||
g = int((hashCode / 65025) % 255)
|
||||
|
||||
63
libs/stringBundle.py
Normal 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()
|
||||
@ -9,7 +9,7 @@ def ustr(x):
|
||||
if type(x) == str:
|
||||
return x.decode(DEFAULT_ENCODING)
|
||||
if type(x) == QString:
|
||||
return unicode(x, DEFAULT_ENCODING)
|
||||
return unicode(x)
|
||||
return x
|
||||
else:
|
||||
return x
|
||||
|
||||
@ -1,35 +1,37 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
|
||||
<file alias="help">icons/help.png</file>
|
||||
<file alias="app">icons/app.png</file>
|
||||
<file alias="expert">icons/expert2.png</file>
|
||||
<file alias="done">icons/done.png</file>
|
||||
<file alias="file">icons/file.png</file>
|
||||
<file alias="labels">icons/labels.png</file>
|
||||
<file alias="new">icons/objects.png</file>
|
||||
<file alias="close">icons/close.png</file>
|
||||
<file alias="fit-width">icons/fit-width.png</file>
|
||||
<file alias="fit-window">icons/fit-window.png</file>
|
||||
<file alias="undo">icons/undo.png</file>
|
||||
<file alias="hide">icons/eye.png</file>
|
||||
<file alias="quit">icons/quit.png</file>
|
||||
<file alias="copy">icons/copy.png</file>
|
||||
<file alias="edit">icons/edit.png</file>
|
||||
<file alias="open">icons/open.png</file>
|
||||
<file alias="save">icons/save.png</file>
|
||||
<file alias="format_voc">icons/format_voc.png</file>
|
||||
<file alias="format_yolo">icons/format_yolo.png</file>
|
||||
<file alias="save-as">icons/save-as.png</file>
|
||||
<file alias="color">icons/color.png</file>
|
||||
<file alias="color_line">icons/color_line.png</file>
|
||||
<file alias="zoom">icons/zoom.png</file>
|
||||
<file alias="zoom-in">icons/zoom-in.png</file>
|
||||
<file alias="zoom-out">icons/zoom-out.png</file>
|
||||
<file alias="delete">icons/cancel.png</file>
|
||||
<file alias="next">icons/next.png</file>
|
||||
<file alias="prev">icons/prev.png</file>
|
||||
<file alias="resetall">icons/resetall.png</file>
|
||||
<file alias="verify">icons/verify.png</file>
|
||||
<file alias="help">resources/icons/help.png</file>
|
||||
<file alias="app">resources/icons/app.png</file>
|
||||
<file alias="expert">resources/icons/expert2.png</file>
|
||||
<file alias="done">resources/icons/done.png</file>
|
||||
<file alias="file">resources/icons/file.png</file>
|
||||
<file alias="labels">resources/icons/labels.png</file>
|
||||
<file alias="new">resources/icons/objects.png</file>
|
||||
<file alias="close">resources/icons/close.png</file>
|
||||
<file alias="fit-width">resources/icons/fit-width.png</file>
|
||||
<file alias="fit-window">resources/icons/fit-window.png</file>
|
||||
<file alias="undo">resources/icons/undo.png</file>
|
||||
<file alias="hide">resources/icons/eye.png</file>
|
||||
<file alias="quit">resources/icons/quit.png</file>
|
||||
<file alias="copy">resources/icons/copy.png</file>
|
||||
<file alias="edit">resources/icons/edit.png</file>
|
||||
<file alias="open">resources/icons/open.png</file>
|
||||
<file alias="save">resources/icons/save.png</file>
|
||||
<file alias="format_voc">resources/icons/format_voc.png</file>
|
||||
<file alias="format_yolo">resources/icons/format_yolo.png</file>
|
||||
<file alias="save-as">resources/icons/save-as.png</file>
|
||||
<file alias="color">resources/icons/color.png</file>
|
||||
<file alias="color_line">resources/icons/color_line.png</file>
|
||||
<file alias="zoom">resources/icons/zoom.png</file>
|
||||
<file alias="zoom-in">resources/icons/zoom-in.png</file>
|
||||
<file alias="zoom-out">resources/icons/zoom-out.png</file>
|
||||
<file alias="delete">resources/icons/cancel.png</file>
|
||||
<file alias="next">resources/icons/next.png</file>
|
||||
<file alias="prev">resources/icons/prev.png</file>
|
||||
<file alias="resetall">resources/icons/resetall.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>
|
||||
</RCC>
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 646 B After Width: | Height: | Size: 646 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 278 B |
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 335 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 765 B After Width: | Height: | Size: 765 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 786 B After Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 675 B After Width: | Height: | Size: 675 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 977 B After Width: | Height: | Size: 977 B |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
65
resources/strings/strings-zh-TW.properties
Normal 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=隱藏所有區塊
|
||||
65
resources/strings/strings.properties
Normal 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
|
||||
2
setup.py
@ -30,7 +30,7 @@ required_packages.append('labelImg')
|
||||
APP = ['labelImg.py']
|
||||
OPTIONS = {
|
||||
'argv_emulation': True,
|
||||
'iconfile': 'icons/app.icns'
|
||||
'iconfile': 'resources/icons/app.icns'
|
||||
}
|
||||
|
||||
setup(
|
||||
|
||||
15
tests/test_lib.py
Normal 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()
|
||||
17
tests/test_stringBundle.py
Normal 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()
|
||||