Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
PaddleOCR
提交
ecf8b569
P
PaddleOCR
项目概览
PaddlePaddle
/
PaddleOCR
大约 1 年 前同步成功
通知
1528
Star
32962
Fork
6643
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
108
列表
看板
标记
里程碑
合并请求
7
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleOCR
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
108
Issue
108
列表
看板
标记
里程碑
合并请求
7
合并请求
7
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
ecf8b569
编写于
2月 05, 2021
作者:
qq_25193841
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
New functions like batch move, delete, withdraw with redundant codes
上级
0ae2b779
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
11071 addition
and
10787 deletion
+11071
-10787
PPOCRLabel/PPOCRLabel.py
PPOCRLabel/PPOCRLabel.py
+168
-55
PPOCRLabel/libs/canvas.py
PPOCRLabel/libs/canvas.py
+281
-130
PPOCRLabel/libs/resources.py
PPOCRLabel/libs/resources.py
+10609
-10598
PPOCRLabel/libs/shape.py
PPOCRLabel/libs/shape.py
+5
-2
PPOCRLabel/resources/strings/strings-zh-CN.properties
PPOCRLabel/resources/strings/strings-zh-CN.properties
+4
-1
PPOCRLabel/resources/strings/strings.properties
PPOCRLabel/resources/strings/strings.properties
+4
-1
未找到文件。
PPOCRLabel/PPOCRLabel.py
浏览文件 @
ecf8b569
...
...
@@ -206,7 +206,7 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
labelList
=
EditInList
()
labelListContainer
=
QWidget
()
labelListContainer
.
setLayout
(
listLayout
)
self
.
labelList
.
itemActivated
.
connect
(
self
.
labelSelectionChanged
)
#
self.labelList.itemActivated.connect(self.labelSelectionChanged)
self
.
labelList
.
itemSelectionChanged
.
connect
(
self
.
labelSelectionChanged
)
self
.
labelList
.
clicked
.
connect
(
self
.
labelList
.
item_clicked
)
# Connect to itemChanged to detect checkbox changes.
...
...
@@ -444,7 +444,7 @@ class MainWindow(QMainWindow, WindowMixin):
'Ctrl+R'
,
'reRec'
,
getStr
(
'singleRe'
),
enabled
=
False
)
createpoly
=
action
(
getStr
(
'creatPolygon'
),
self
.
createPolygon
,
'q'
,
'new'
,
'Creat Polygon'
,
enabled
=
True
)
'q'
,
'new'
,
getStr
(
'creatPolygon'
)
,
enabled
=
True
)
saveRec
=
action
(
getStr
(
'saveRec'
),
self
.
saveRecResult
,
''
,
'save'
,
getStr
(
'saveRec'
),
enabled
=
False
)
...
...
@@ -452,6 +452,12 @@ class MainWindow(QMainWindow, WindowMixin):
saveLabel
=
action
(
getStr
(
'saveLabel'
),
self
.
saveLabelFile
,
#
'Ctrl+S'
,
'save'
,
getStr
(
'saveLabel'
),
enabled
=
False
)
undoLastPoint
=
action
(
getStr
(
"undoLastPoint"
),
self
.
canvas
.
undoLastPoint
,
'Ctrl+Z'
,
"undo"
,
"Undo last drawn point"
,
enabled
=
False
)
undo
=
action
(
getStr
(
"undo"
),
self
.
undoShapeEdit
,
'Ctrl+Z'
,
"undo"
,
"Undo last add and edit of shape"
,
enabled
=
False
)
self
.
editButton
.
setDefaultAction
(
edit
)
self
.
newButton
.
setDefaultAction
(
create
)
self
.
DelButton
.
setDefaultAction
(
deleteImg
)
...
...
@@ -512,10 +518,11 @@ class MainWindow(QMainWindow, WindowMixin):
zoom
=
zoom
,
zoomIn
=
zoomIn
,
zoomOut
=
zoomOut
,
zoomOrg
=
zoomOrg
,
fitWindow
=
fitWindow
,
fitWidth
=
fitWidth
,
zoomActions
=
zoomActions
,
saveLabel
=
saveLabel
,
undo
=
undo
,
undoLastPoint
=
undoLastPoint
,
fileMenuActions
=
(
opendir
,
saveLabel
,
resetAll
,
quit
),
beginner
=
(),
advanced
=
(),
editMenu
=
(
createpoly
,
edit
,
copy
,
delete
,
singleRere
,
editMenu
=
(
createpoly
,
edit
,
copy
,
delete
,
singleRere
,
None
,
undo
,
undoLastPoint
,
None
,
color1
,
self
.
drawSquaresOption
),
beginnerContext
=
(
create
,
edit
,
copy
,
delete
,
singleRere
),
advancedContext
=
(
createMode
,
editMode
,
edit
,
copy
,
...
...
@@ -549,8 +556,13 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
labelDialogOption
.
setChecked
(
settings
.
get
(
SETTING_PAINT_LABEL
,
False
))
self
.
labelDialogOption
.
triggered
.
connect
(
self
.
speedChoose
)
self
.
autoSaveOption
=
QAction
(
getStr
(
'autoSaveMode'
),
self
)
self
.
autoSaveOption
.
setCheckable
(
True
)
self
.
autoSaveOption
.
setChecked
(
settings
.
get
(
SETTING_PAINT_LABEL
,
False
))
self
.
autoSaveOption
.
triggered
.
connect
(
self
.
autoSaveFunc
)
addActions
(
self
.
menus
.
file
,
(
opendir
,
None
,
saveLabel
,
saveRec
,
None
,
resetAll
,
deleteImg
,
quit
))
(
opendir
,
None
,
saveLabel
,
saveRec
,
self
.
autoSaveOption
,
None
,
resetAll
,
deleteImg
,
quit
))
addActions
(
self
.
menus
.
help
,
(
showSteps
,
showInfo
))
addActions
(
self
.
menus
.
view
,
(
...
...
@@ -758,6 +770,7 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
canvas
.
setEditing
(
False
)
self
.
canvas
.
fourpoint
=
True
self
.
actions
.
create
.
setEnabled
(
False
)
self
.
actions
.
undoLastPoint
.
setEnabled
(
True
)
def
toggleDrawingSensitive
(
self
,
drawing
=
True
):
"""In the middle of drawing, toggling between modes should be disabled."""
...
...
@@ -865,11 +878,21 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
setDirty
()
self
.
updateComboBox
()
# def updateBoxlist(self):
# shape = self.canvas.selectedShape
# item = self.shapesToItemsbox[shape] # listitem
# text = [(int(p.x()), int(p.y())) for p in shape.points]
# item.setText(str(text))
# self.setDirty()
def
updateBoxlist
(
self
):
shape
=
self
.
canvas
.
selectedShape
item
=
self
.
shapesToItemsbox
[
shape
]
# listitem
text
=
[(
int
(
p
.
x
()),
int
(
p
.
y
()))
for
p
in
shape
.
points
]
item
.
setText
(
str
(
text
))
#changedShape = self.canvas.selectedShapes
#changedShape.append(self.canvas.hShape) #changedShape: #
for
shape
in
self
.
canvas
.
selectedShapes
+
[
self
.
canvas
.
hShape
]:
item
=
self
.
shapesToItemsbox
[
shape
]
# listitem
text
=
[(
int
(
p
.
x
()),
int
(
p
.
y
()))
for
p
in
shape
.
points
]
item
.
setText
(
str
(
text
))
self
.
actions
.
undo
.
setEnabled
(
True
)
self
.
setDirty
()
def
indexTo5Files
(
self
,
currIndex
):
...
...
@@ -902,23 +925,60 @@ class MainWindow(QMainWindow, WindowMixin):
if
len
(
self
.
mImgList
)
>
0
:
self
.
zoomWidget
.
setValue
(
self
.
zoomWidgetValue
+
self
.
imgsplider
.
value
())
# React to canvas signals.
def
shapeSelectionChanged
(
self
,
selected
=
False
):
if
self
.
_noSelectionSlot
:
self
.
_noSelectionSlot
=
False
else
:
shape
=
self
.
canvas
.
selectedShape
if
shape
:
self
.
shapesToItems
[
shape
].
setSelected
(
True
)
self
.
shapesToItemsbox
[
shape
].
setSelected
(
True
)
# ADD
else
:
self
.
labelList
.
clearSelection
()
self
.
actions
.
delete
.
setEnabled
(
selected
)
self
.
actions
.
copy
.
setEnabled
(
selected
)
self
.
actions
.
edit
.
setEnabled
(
selected
)
self
.
actions
.
shapeLineColor
.
setEnabled
(
selected
)
self
.
actions
.
shapeFillColor
.
setEnabled
(
selected
)
self
.
actions
.
singleRere
.
setEnabled
(
selected
)
# # TODO: UPDATE THIS FUNCTION
# # React to canvas signals.
# def shapeSelectionChanged(self, selected=False):
# if self._noSelectionSlot:
# self._noSelectionSlot = False
# else:
# shape = self.canvas.selectedShape
# if shape:
# self.shapesToItems[shape].setSelected(True)
# self.shapesToItemsbox[shape].setSelected(True) # ADD
# else:
# self.labelList.clearSelection()
# self.actions.delete.setEnabled(selected)
# self.actions.copy.setEnabled(selected)
# self.actions.edit.setEnabled(selected)
# self.actions.shapeLineColor.setEnabled(selected)
# self.actions.shapeFillColor.setEnabled(selected)
# self.actions.singleRere.setEnabled(selected)
# def shapeSelectionChanged(self, selected_shapes):
# if self._noSelectionSlot:
# self._noSelectionSlot = False
# else:
# if self.canvas.selectedShapes:
# for shape in self.canvas.selectedShapes:
# self.shapesToItems[shape].setSelected(True)
# self.shapesToItemsbox[shape].setSelected(True)
# else:
# self.labelList.clearSelection()
#
# n_selected = len(selected_shapes)
# self.actions.delete.setEnabled(n_selected)
# self.actions.copy.setEnabled(n_selected)
# self.actions.edit.setEnabled(n_selected == 1)
def
shapeSelectionChanged
(
self
,
selected_shapes
):
self
.
_noSelectionSlot
=
True
for
shape
in
self
.
canvas
.
selectedShapes
:
# 为何要反选?
shape
.
selected
=
False
self
.
labelList
.
clearSelection
()
self
.
canvas
.
selectedShapes
=
selected_shapes
# 这里没有把选择的两个都加入
for
shape
in
self
.
canvas
.
selectedShapes
:
shape
.
selected
=
True
# item = self.labelList.findItemByShape(shape)
# self.labelList.selectItem(item)
# self.labelList.scrollToItem(item)
self
.
shapesToItems
[
shape
].
setSelected
(
True
)
self
.
shapesToItemsbox
[
shape
].
setSelected
(
True
)
# ADD 是否可以代替selectItem?
self
.
_noSelectionSlot
=
False
n_selected
=
len
(
selected_shapes
)
self
.
actions
.
delete
.
setEnabled
(
n_selected
)
self
.
actions
.
copy
.
setEnabled
(
n_selected
)
self
.
actions
.
edit
.
setEnabled
(
n_selected
==
1
)
def
addLabel
(
self
,
shape
):
shape
.
paintLabel
=
self
.
displayLabelOption
.
isChecked
()
...
...
@@ -941,22 +1001,23 @@ class MainWindow(QMainWindow, WindowMixin):
action
.
setEnabled
(
True
)
self
.
updateComboBox
()
def
remLabel
(
self
,
shape
):
if
shape
is
None
:
def
remLabel
s
(
self
,
shapes
):
if
shape
s
is
None
:
# print('rm empty label')
return
item
=
self
.
shapesToItems
[
shape
]
self
.
labelList
.
takeItem
(
self
.
labelList
.
row
(
item
))
del
self
.
shapesToItems
[
shape
]
del
self
.
itemsToShapes
[
item
]
self
.
updateComboBox
()
for
shape
in
shapes
:
item
=
self
.
shapesToItems
[
shape
]
self
.
labelList
.
takeItem
(
self
.
labelList
.
row
(
item
))
del
self
.
shapesToItems
[
shape
]
del
self
.
itemsToShapes
[
item
]
self
.
updateComboBox
()
# ADD:
item
=
self
.
shapesToItemsbox
[
shape
]
self
.
BoxList
.
takeItem
(
self
.
BoxList
.
row
(
item
))
del
self
.
shapesToItemsbox
[
shape
]
del
self
.
itemsToShapesbox
[
item
]
self
.
updateComboBox
()
# ADD:
item
=
self
.
shapesToItemsbox
[
shape
]
self
.
BoxList
.
takeItem
(
self
.
BoxList
.
row
(
item
))
del
self
.
shapesToItemsbox
[
shape
]
del
self
.
itemsToShapesbox
[
item
]
self
.
updateComboBox
()
def
loadLabels
(
self
,
shapes
):
s
=
[]
...
...
@@ -1001,7 +1062,7 @@ class MainWindow(QMainWindow, WindowMixin):
item
.
setText
(
str
([(
int
(
p
.
x
()),
int
(
p
.
y
()))
for
p
in
shape
.
points
]))
self
.
updateComboBox
()
def
updateComboBox
(
self
):
def
updateComboBox
(
self
):
# TODO:貌似没用
# Get the unique labels and add them to the Combobox.
itemsTextList
=
[
str
(
self
.
labelList
.
item
(
i
).
text
())
for
i
in
range
(
self
.
labelList
.
count
())]
...
...
@@ -1059,21 +1120,47 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
shapeSelectionChanged
(
True
)
# def labelSelectionChanged(self):
# item = self.currentItem()
# self.labelList.scrollToItem(item, QAbstractItemView.EnsureVisible)
# if item and self.canvas.editing():
# self._noSelectionSlot = True
# self.canvas.selectShape(self.itemsToShapes[item])
# shape = self.itemsToShapes[item]
def
labelSelectionChanged
(
self
):
item
=
self
.
currentItem
()
self
.
labelList
.
scrollToItem
(
item
,
QAbstractItemView
.
EnsureVisible
)
if
item
and
self
.
canvas
.
editing
():
self
.
_noSelectionSlot
=
True
self
.
canvas
.
selectShape
(
self
.
itemsToShapes
[
item
])
shape
=
self
.
itemsToShapes
[
item
]
if
self
.
_noSelectionSlot
:
return
if
self
.
canvas
.
editing
():
selected_shapes
=
[]
for
item
in
self
.
labelList
.
selectedItems
():
selected_shapes
.
append
(
self
.
itemsToShapes
[
item
])
if
selected_shapes
:
self
.
canvas
.
selectShapes
(
selected_shapes
)
else
:
self
.
canvas
.
deSelectShape
()
# def boxSelectionChanged(self):
# item = self.currentBox()
# self.BoxList.scrollToItem(item, QAbstractItemView.EnsureVisible)
# if item and self.canvas.editing():
# self._noSelectionSlot = True
# self.canvas.selectShape(self.itemsToShapesbox[item])
# shape = self.itemsToShapesbox[item]
def
boxSelectionChanged
(
self
):
item
=
self
.
currentBox
()
self
.
BoxList
.
scrollToItem
(
item
,
QAbstractItemView
.
EnsureVisible
)
if
item
and
self
.
canvas
.
editing
():
self
.
_noSelectionSlot
=
True
self
.
canvas
.
selectShape
(
self
.
itemsToShapesbox
[
item
])
shape
=
self
.
itemsToShapesbox
[
item
]
if
self
.
_noSelectionSlot
:
return
if
self
.
canvas
.
editing
():
selected_shapes
=
[]
for
item
in
self
.
labelList
.
selectedItems
():
selected_shapes
.
append
(
self
.
itemsToShapesbox
[
item
])
if
selected_shapes
:
self
.
canvas
.
selectShapes
(
selected_shapes
)
else
:
self
.
canvas
.
deSelectShape
()
def
labelItemChanged
(
self
,
item
):
shape
=
self
.
itemsToShapes
[
item
]
...
...
@@ -1113,6 +1200,8 @@ class MainWindow(QMainWindow, WindowMixin):
if
self
.
beginner
():
# Switch to edit mode.
self
.
canvas
.
setEditing
(
True
)
self
.
actions
.
create
.
setEnabled
(
True
)
self
.
actions
.
undoLastPoint
.
setEnabled
(
False
)
self
.
actions
.
undo
.
setEnabled
(
True
)
else
:
self
.
actions
.
editMode
.
setEnabled
(
True
)
self
.
setDirty
()
...
...
@@ -1643,7 +1732,8 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
setDirty
()
def
deleteSelectedShape
(
self
):
self
.
remLabel
(
self
.
canvas
.
deleteSelected
())
self
.
remLabels
(
self
.
canvas
.
deleteSelected
())
self
.
actions
.
undo
.
setEnabled
(
True
)
self
.
setDirty
()
if
self
.
noShapes
():
for
action
in
self
.
actions
.
onShapesPresent
:
...
...
@@ -1914,8 +2004,8 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
savePPlabel
()
def
saveRecResult
(
self
):
if
None
in
[
self
.
PPlabelpath
,
self
.
PPlabel
,
self
.
fileStatedict
]:
QMessageBox
.
information
(
self
,
"Information"
,
"
Save fil
e first"
)
if
{}
in
[
self
.
PPlabelpath
,
self
.
PPlabel
,
self
.
fileStatedict
]:
QMessageBox
.
information
(
self
,
"Information"
,
"
Check the imag
e first"
)
return
rec_gt_dir
=
os
.
path
.
dirname
(
self
.
PPlabelpath
)
+
'/rec_gt.txt'
...
...
@@ -1953,6 +2043,29 @@ class MainWindow(QMainWindow, WindowMixin):
self
.
canvas
.
newShape
.
disconnect
()
self
.
canvas
.
newShape
.
connect
(
partial
(
self
.
newShape
,
False
))
def
autoSaveFunc
(
self
):
if
self
.
autoSaveOption
.
isChecked
():
self
.
autoSaveNum
=
1
# Real auto_Save
print
(
'The program will automatically save once after confirming an image'
)
else
:
self
.
autoSaveNum
=
5
# Used for backup
print
(
'The program will automatically save once after confirming 5 images (default)'
)
def
undoShapeEdit
(
self
):
self
.
canvas
.
restoreShape
()
self
.
labelList
.
clear
()
self
.
BoxList
.
clear
()
self
.
loadShapes
(
self
.
canvas
.
shapes
)
# 重新加载
self
.
actions
.
undo
.
setEnabled
(
self
.
canvas
.
isShapeRestorable
)
def
loadShapes
(
self
,
shapes
,
replace
=
True
):
self
.
_noSelectionSlot
=
True
for
shape
in
shapes
:
self
.
addLabel
(
shape
)
self
.
labelList
.
clearSelection
()
self
.
_noSelectionSlot
=
False
self
.
canvas
.
loadShapes
(
shapes
,
replace
=
replace
)
def
inverted
(
color
):
return
QColor
(
*
[
255
-
v
for
v
in
color
.
getRgb
()])
...
...
@@ -1976,7 +2089,7 @@ def get_main_app(argv=[]):
app
.
setWindowIcon
(
newIcon
(
"app"
))
# Tzutalin 201705+: Accept extra agruments to change predefined class file
argparser
=
argparse
.
ArgumentParser
()
argparser
.
add_argument
(
"--lang"
,
default
=
'
en
'
,
nargs
=
"?"
)
argparser
.
add_argument
(
"--lang"
,
default
=
'
ch
'
,
nargs
=
"?"
)
argparser
.
add_argument
(
"--predefined_classes_file"
,
default
=
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
"data"
,
"predefined_classes.txt"
),
nargs
=
"?"
)
...
...
PPOCRLabel/libs/canvas.py
浏览文件 @
ecf8b569
...
...
@@ -37,7 +37,8 @@ class Canvas(QWidget):
zoomRequest
=
pyqtSignal
(
int
)
scrollRequest
=
pyqtSignal
(
int
,
int
)
newShape
=
pyqtSignal
()
selectionChanged
=
pyqtSignal
(
bool
)
# selectionChanged = pyqtSignal(bool)
selectionChanged
=
pyqtSignal
(
list
)
shapeMoved
=
pyqtSignal
()
drawingPolygon
=
pyqtSignal
(
bool
)
...
...
@@ -51,9 +52,11 @@ class Canvas(QWidget):
# Initialise local state.
self
.
mode
=
self
.
EDIT
self
.
shapes
=
[]
self
.
shapesBackups
=
[]
self
.
current
=
None
self
.
selectedShapes
=
[]
self
.
selectedShape
=
None
# save the selected shape here
self
.
selectedShape
Copy
=
None
self
.
selectedShape
sCopy
=
[]
self
.
drawingLineColor
=
QColor
(
0
,
0
,
255
)
self
.
drawingRectColor
=
QColor
(
0
,
0
,
255
)
self
.
line
=
Shape
(
line_color
=
self
.
drawingLineColor
)
...
...
@@ -77,6 +80,7 @@ class Canvas(QWidget):
self
.
drawSquare
=
False
self
.
fourpoint
=
True
# ADD
self
.
pointnum
=
0
self
.
movingShape
=
False
#initialisation for panning
self
.
pan_initial_pos
=
QPoint
()
...
...
@@ -149,37 +153,40 @@ class Canvas(QWidget):
clipped_x
=
min
(
max
(
0
,
pos
.
x
()),
size
.
width
())
clipped_y
=
min
(
max
(
0
,
pos
.
y
()),
size
.
height
())
pos
=
QPointF
(
clipped_x
,
clipped_y
)
elif
len
(
self
.
current
)
>
1
and
self
.
closeEnough
(
pos
,
self
.
current
[
0
])
and
not
self
.
fourpoint
:
elif
len
(
self
.
current
)
>
1
and
self
.
closeEnough
(
pos
,
self
.
current
[
0
]):
# and not self.fourpoint:
# Attract line to starting point and colorise to alert the
# user:
pos
=
self
.
current
[
0
]
color
=
self
.
current
.
line_color
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
current
.
highlightVertex
(
0
,
Shape
.
NEAR_VERTEX
)
elif
(
# ADD
len
(
self
.
current
)
>
1
and
self
.
fourpoint
and
self
.
closeEnough
(
pos
,
self
.
current
[
0
])
):
# Attract line to starting point and
# colorise to alert the user.
pos
=
self
.
current
[
0
]
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
current
.
highlightVertex
(
0
,
Shape
.
NEAR_VERTEX
)
if
self
.
drawSquare
:
initPos
=
self
.
current
[
0
]
minX
=
initPos
.
x
()
minY
=
initPos
.
y
()
min_size
=
min
(
abs
(
pos
.
x
()
-
minX
),
abs
(
pos
.
y
()
-
minY
))
directionX
=
-
1
if
pos
.
x
()
-
minX
<
0
else
1
directionY
=
-
1
if
pos
.
y
()
-
minY
<
0
else
1
self
.
line
[
1
]
=
QPointF
(
minX
+
directionX
*
min_size
,
minY
+
directionY
*
min_size
)
# elif ( # ADD # 合并上下代码 内容一样
# len(self.current) > 1
# and self.fourpoint
# and self.closeEnough(pos, self.current[0])
# ):
# # Attract line to starting point and
# # colorise to alert the user.
# pos = self.current[0]
# self.overrideCursor(CURSOR_POINT)
# self.current.highlightVertex(0, Shape.NEAR_VERTEX)
if
self
.
drawSquare
:
# 这部分不同
# initPos = self.current[0] # 原先代码
# minX = initPos.x()
# minY = initPos.y()
# min_size = min(abs(pos.x() - minX), abs(pos.y() - minY))
# directionX = -1 if pos.x() - minX < 0 else 1
# directionY = -1 if pos.y() - minY < 0 else 1
# self.line[1] = QPointF(minX + directionX * min_size, minY + directionY * min_size)
self
.
line
.
points
=
[
self
.
current
[
0
],
pos
]
# Labelme代码
self
.
line
.
close
()
elif
self
.
fourpoint
:
# self.line[self.pointnum] = pos # OLD
self
.
line
[
0
]
=
self
.
current
[
-
1
]
self
.
line
[
1
]
=
pos
...
...
@@ -193,15 +200,17 @@ class Canvas(QWidget):
self
.
prevPoint
=
pos
self
.
repaint
()
return
# 一下都相同
# Polygon copy moving.
if
Qt
.
RightButton
&
ev
.
buttons
():
if
self
.
selectedShapeCopy
and
self
.
prevPoint
:
if
self
.
selectedShape
s
Copy
and
self
.
prevPoint
:
self
.
overrideCursor
(
CURSOR_MOVE
)
self
.
boundedMoveShape
(
self
.
selectedShapeCopy
,
pos
)
self
.
boundedMoveShape
(
self
.
selectedShape
s
Copy
,
pos
)
self
.
repaint
()
elif
self
.
selectedShape
:
self
.
selectedShapeCopy
=
self
.
selectedShape
.
copy
()
elif
self
.
selectedShapes
:
self
.
selectedShapesCopy
=
[
s
.
copy
()
for
s
in
self
.
selectedShapes
]
self
.
repaint
()
return
...
...
@@ -209,14 +218,16 @@ class Canvas(QWidget):
if
Qt
.
LeftButton
&
ev
.
buttons
():
if
self
.
selectedVertex
():
self
.
boundedMoveVertex
(
pos
)
self
.
shapeMoved
.
emit
()
self
.
shapeMoved
.
emit
()
# 同时选中时的移动
self
.
repaint
()
elif
self
.
selectedShape
and
self
.
prevPoint
:
self
.
movingShape
=
True
elif
self
.
selectedShapes
and
self
.
prevPoint
:
self
.
overrideCursor
(
CURSOR_MOVE
)
self
.
boundedMoveShape
(
self
.
selectedShape
,
pos
)
self
.
boundedMoveShape
(
self
.
selectedShape
s
,
pos
)
self
.
shapeMoved
.
emit
()
self
.
repaint
()
else
:
self
.
movingShape
=
True
else
:
# TODO 这部分是多的
#pan
delta_x
=
pos
.
x
()
-
self
.
pan_initial_pos
.
x
()
delta_y
=
pos
.
y
()
-
self
.
pan_initial_pos
.
y
()
...
...
@@ -237,7 +248,8 @@ class Canvas(QWidget):
if
index
is
not
None
:
if
self
.
selectedVertex
():
self
.
hShape
.
highlightClear
()
self
.
hVertex
,
self
.
hShape
=
index
,
shape
# TODO: Pre部分的变量都没有
self
.
hVertex
,
self
.
hShape
=
index
,
shape
# 倒着来的原因是要更新hShape的值?
shape
.
highlightVertex
(
index
,
shape
.
MOVE_VERTEX
)
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
setToolTip
(
"Click & drag to move point"
)
...
...
@@ -263,23 +275,25 @@ class Canvas(QWidget):
def
mousePressEvent
(
self
,
ev
):
pos
=
self
.
transformPos
(
ev
.
pos
())
if
ev
.
button
()
==
Qt
.
LeftButton
:
if
self
.
drawing
():
# self.handleDrawing(pos) # OLD
if
self
.
current
and
self
.
fourpoint
:
# ADD IF
# Add point to existing shape.
print
(
'Adding points in mousePressEvent is '
,
self
.
line
[
1
])
self
.
current
.
addPoint
(
self
.
line
[
1
])
self
.
line
[
0
]
=
self
.
current
[
-
1
]
if
self
.
current
.
isClosed
():
# print('1111')
if
self
.
current
:
if
self
.
fourpoint
:
# ADD IF
# Add point to existing shape.
# print('Adding points in mousePressEvent is ', self.line[1])
self
.
current
.
addPoint
(
self
.
line
[
1
])
self
.
line
[
0
]
=
self
.
current
[
-
1
]
if
self
.
current
.
isClosed
():
# print('1111')
self
.
finalise
()
elif
self
.
drawSquare
:
# 增加
assert
len
(
self
.
current
.
points
)
==
1
self
.
current
.
points
=
self
.
line
.
points
self
.
finalise
()
elif
not
self
.
outOfPixmap
(
pos
):
# Create new shape.
self
.
current
=
Shape
()
# self.current = Shape(shape_type=self.createMode)
self
.
current
=
Shape
()
# self.current = Shape(shape_type=self.createMode)
# TODO: 有可能需要制定类型?
self
.
current
.
addPoint
(
pos
)
# if self.createMode == "point":
# self.finalise()
...
...
@@ -291,69 +305,103 @@ class Canvas(QWidget):
self
.
drawingPolygon
.
emit
(
True
)
self
.
update
()
else
:
selection
=
self
.
selectShapePoint
(
pos
)
else
:
# 改动后可以增加多选框,选点方式从单点变成list
# selection = self.selectShapePoint(pos)
# self.prevPoint = pos
#
# if selection is None:
# #pan
# QApplication.setOverrideCursor(QCursor(Qt.OpenHandCursor))
# self.pan_initial_pos = pos
group_mode
=
int
(
ev
.
modifiers
())
==
Qt
.
ControlModifier
self
.
selectShapePoint
(
pos
,
multiple_selection_mode
=
group_mode
)
self
.
prevPoint
=
pos
if
selection
is
None
:
#pan
QApplication
.
setOverrideCursor
(
QCursor
(
Qt
.
OpenHandCursor
))
self
.
pan_initial_pos
=
pos
self
.
pan_initial_pos
=
pos
# self.repaint()
elif
ev
.
button
()
==
Qt
.
RightButton
and
self
.
editing
():
self
.
selectShapePoint
(
pos
)
# self.selectShapePoint(pos)
# self.prevPoint = pos
group_mode
=
int
(
ev
.
modifiers
())
==
Qt
.
ControlModifier
self
.
selectShapePoint
(
pos
,
multiple_selection_mode
=
group_mode
)
self
.
prevPoint
=
pos
# self.repaint() # 只用update?
self
.
update
()
def
mouseReleaseEvent
(
self
,
ev
):
if
ev
.
button
()
==
Qt
.
RightButton
:
menu
=
self
.
menus
[
bool
(
self
.
selectedShapeCopy
)]
menu
=
self
.
menus
[
bool
(
self
.
selectedShape
s
Copy
)]
self
.
restoreCursor
()
if
not
menu
.
exec_
(
self
.
mapToGlobal
(
ev
.
pos
()))
\
and
self
.
selectedShapeCopy
:
and
self
.
selectedShape
s
Copy
:
# Cancel the move by deleting the shadow copy.
self
.
selectedShapeCopy
=
None
# self.selectedShapeCopy = None
self
.
selectedShapesCopy
=
[]
self
.
repaint
()
elif
ev
.
button
()
==
Qt
.
LeftButton
and
self
.
selectedShape
:
# OLD
# elif ev.button() == Qt.LeftButton and self.selectedShape: # OLD
elif
ev
.
button
()
==
Qt
.
LeftButton
and
self
.
selectedShapes
:
if
self
.
selectedVertex
():
self
.
overrideCursor
(
CURSOR_POINT
)
else
:
self
.
overrideCursor
(
CURSOR_GRAB
)
elif
ev
.
button
()
==
Qt
.
LeftButton
and
not
self
.
fourpoint
:
elif
ev
.
button
()
==
Qt
.
LeftButton
and
not
self
.
fourpoint
:
# 暂时去除四点部分的代码
pos
=
self
.
transformPos
(
ev
.
pos
())
if
self
.
drawing
():
self
.
handleDrawing
(
pos
)
self
.
handleDrawing
(
pos
)
# 关键函数
else
:
#pan
QApplication
.
restoreOverrideCursor
()
# ?
if
self
.
movingShape
and
self
.
hShape
:
# 加上之后会移动点会崩 用于撤回
index
=
self
.
shapes
.
index
(
self
.
hShape
)
if
(
self
.
shapesBackups
[
-
1
][
index
].
points
# 如果新建的框位置变化
!=
self
.
shapes
[
index
].
points
):
self
.
storeShapes
()
# 重新backup一下
self
.
shapeMoved
.
emit
()
# 连接updateBoxlist
self
.
movingShape
=
False
def
endMove
(
self
,
copy
=
False
):
assert
self
.
selectedShape
and
self
.
selectedShapeCopy
shape
=
self
.
selectedShapeCopy
#
assert self.selectedShape and self.selectedShapeCopy
#
shape = self.selectedShapeCopy
#del shape.fill_color
#del shape.line_color
# if copy:
# self.shapes.append(shape)
# self.selectedShape.selected = False
# self.selectedShape = shape
# self.repaint()
# else:
# self.selectedShape.points = [p for p in shape.points]
# self.selectedShapeCopy = None
assert
self
.
selectedShapes
and
self
.
selectedShapesCopy
assert
len
(
self
.
selectedShapesCopy
)
==
len
(
self
.
selectedShapes
)
if
copy
:
self
.
shapes
.
append
(
shape
)
self
.
selectedShape
.
selected
=
False
self
.
selectedShape
=
shap
e
self
.
repaint
()
for
i
,
shape
in
enumerate
(
self
.
selectedShapesCopy
):
self
.
shapes
.
append
(
shape
)
self
.
selectedShapes
[
i
].
selected
=
Fals
e
self
.
selectedShapes
[
i
]
=
shape
else
:
self
.
selectedShape
.
points
=
[
p
for
p
in
shape
.
points
]
self
.
selectedShapeCopy
=
None
for
i
,
shape
in
enumerate
(
self
.
selectedShapesCopy
):
self
.
selectedShapes
[
i
].
points
=
shape
.
points
self
.
selectedShapesCopy
=
[]
self
.
repaint
()
self
.
storeShapes
()
return
True
def
hideBackroundShapes
(
self
,
value
):
self
.
hideBackround
=
value
if
self
.
selectedShape
:
if
self
.
selectedShape
s
:
# Only hide other shapes if there is a current selection.
# Otherwise the user will not be able to select a shape.
self
.
setHiding
(
True
)
self
.
repaint
()
def
handleDrawing
(
self
,
pos
):
def
handleDrawing
(
self
,
pos
):
# 没有此函数
if
self
.
current
and
self
.
current
.
reachMaxPoints
()
is
False
:
if
self
.
fourpoint
:
targetPos
=
self
.
line
[
self
.
pointnum
]
...
...
@@ -399,28 +447,54 @@ class Canvas(QWidget):
self
.
current
.
popPoint
()
self
.
finalise
()
def
selectShape
(
self
,
shape
):
self
.
deSelectShape
()
shape
.
selected
=
True
self
.
selectedShape
=
shape
def
selectShapes
(
self
,
shapes
):
# self.deSelectShape()
# shape.selected = True
# self.selectedShape = shape
# self.setHiding()
# self.selectionChanged.emit(True)
# self.update()
for
s
in
shapes
:
s
.
seleted
=
True
self
.
setHiding
()
self
.
selectionChanged
.
emit
(
True
)
self
.
selectionChanged
.
emit
(
shapes
)
self
.
update
()
def
selectShapePoint
(
self
,
point
):
# def selectShapePoint(self, point):
# """Select the first shape created which contains this point."""
# self.deSelectShape()
# if self.selectedVertex(): # A vertex is marked for selection.
# index, shape = self.hVertex, self.hShape
# shape.highlightVertex(index, shape.MOVE_VERTEX)
# self.selectShape(shape)
# return self.hVertex
# for shape in reversed(self.shapes):
# if self.isVisible(shape) and shape.containsPoint(point):
# self.selectShape(shape) # 函数
# self.calculateOffsets(shape, point)
# return self.selectedShape
# return None
def
selectShapePoint
(
self
,
point
,
multiple_selection_mode
):
"""Select the first shape created which contains this point."""
self
.
deSelectShape
()
if
self
.
selectedVertex
():
# A vertex is marked for selection.
index
,
shape
=
self
.
hVertex
,
self
.
hShape
shape
.
highlightVertex
(
index
,
shape
.
MOVE_VERTEX
)
self
.
selectShape
(
shape
)
shape
.
highlightVertex
(
index
,
shape
.
MOVE_VERTEX
)
# 突出显示
return
self
.
hVertex
for
shape
in
reversed
(
self
.
shapes
):
if
self
.
isVisible
(
shape
)
and
shape
.
containsPoint
(
point
):
self
.
selectShape
(
shape
)
self
.
calculateOffsets
(
shape
,
point
)
return
self
.
selectedShape
return
None
else
:
for
shape
in
reversed
(
self
.
shapes
):
if
self
.
isVisible
(
shape
)
and
shape
.
containsPoint
(
point
):
self
.
calculateOffsets
(
shape
,
point
)
self
.
setHiding
()
if
multiple_selection_mode
:
if
shape
not
in
self
.
selectedShapes
:
# list TODO:为什么是2个,刚开始应该是1个
self
.
selectionChanged
.
emit
(
self
.
selectedShapes
+
[
shape
]
# 选择+未选择
)
else
:
self
.
selectionChanged
.
emit
([
shape
])
return
self
.
deSelectShape
()
def
calculateOffsets
(
self
,
shape
,
point
):
rect
=
shape
.
boundingRect
()
...
...
@@ -465,22 +539,26 @@ class Canvas(QWidget):
else
:
shiftPos
=
pos
-
point
shape
.
moveVertexBy
(
index
,
shiftPos
)
if
shape
[
0
].
x
()
==
shape
[
3
].
x
()
and
shape
[
1
].
x
()
==
shape
[
2
].
x
()
and
shape
[
0
].
y
()
==
shape
[
1
].
y
():
shape
.
moveVertexBy
(
index
,
shiftPos
)
lindex
=
(
index
+
1
)
%
4
rindex
=
(
index
+
3
)
%
4
lshift
=
None
rshift
=
None
if
index
%
2
==
0
:
rshift
=
QPointF
(
shiftPos
.
x
(),
0
)
lshift
=
QPointF
(
0
,
shiftPos
.
y
())
else
:
lshift
=
QPointF
(
shiftPos
.
x
(),
0
)
rshift
=
QPointF
(
0
,
shiftPos
.
y
())
shape
.
moveVertexBy
(
rindex
,
rshift
)
shape
.
moveVertexBy
(
lindex
,
lshift
)
lindex
=
(
index
+
1
)
%
4
rindex
=
(
index
+
3
)
%
4
lshift
=
None
rshift
=
None
if
index
%
2
==
0
:
rshift
=
QPointF
(
shiftPos
.
x
(),
0
)
lshift
=
QPointF
(
0
,
shiftPos
.
y
())
else
:
lshift
=
QPointF
(
shiftPos
.
x
(),
0
)
rshift
=
QPointF
(
0
,
shiftPos
.
y
())
shape
.
moveVertexBy
(
rindex
,
rshift
)
shape
.
moveVertexBy
(
lindex
,
lshift
)
shape
.
moveVertexBy
(
index
,
shiftPos
)
def
boundedMoveShape
(
self
,
shape
,
pos
):
def
boundedMoveShape
(
self
,
shape
s
,
pos
):
if
self
.
outOfPixmap
(
pos
):
return
False
# No need to move
o1
=
pos
+
self
.
offsets
[
0
]
...
...
@@ -497,36 +575,69 @@ class Canvas(QWidget):
#self.calculateOffsets(self.selectedShape, pos)
dp
=
pos
-
self
.
prevPoint
if
dp
:
shape
.
moveBy
(
dp
)
for
shape
in
shapes
:
shape
.
moveBy
(
dp
)
self
.
prevPoint
=
pos
return
True
return
False
def
deSelectShape
(
self
):
if
self
.
selectedShape
:
self
.
selectedShape
.
selected
=
False
self
.
selectedShape
=
None
# if self.selectedShape:
# self.selectedShape.selected = False
# self.selectedShape = None
# self.setHiding(False)
# self.selectionChanged.emit(False)
# self.update()
if
self
.
selectedShapes
:
# TODO:少了两个清空?
for
shape
in
self
.
selectedShapes
:
shape
.
selected
=
False
self
.
setHiding
(
False
)
self
.
selectionChanged
.
emit
(
False
)
self
.
selectionChanged
.
emit
(
[]
)
self
.
update
()
# def deleteSelected(self):
# if self.selectedShape:
# shape = self.selectedShape
# self.shapes.remove(self.selectedShape)
# self.selectedShape = None
# self.update()
# return shape
def
deleteSelected
(
self
):
if
self
.
selectedShape
:
shape
=
self
.
selectedShape
self
.
shapes
.
remove
(
self
.
selectedShape
)
self
.
selectedShape
=
None
deleted_shapes
=
[]
if
self
.
selectedShapes
:
#self.storeShapes()
for
shape
in
self
.
selectedShapes
:
self
.
shapes
.
remove
(
shape
)
#self.shapesBackups.append(shape)
deleted_shapes
.
append
(
shape
)
self
.
storeShapes
()
# 这里应该是先储存
self
.
selectedShapes
=
[]
self
.
update
()
return
shape
return
deleted_shapes
def
storeShapes
(
self
):
shapesBackup
=
[]
for
shape
in
self
.
shapes
:
shapesBackup
.
append
(
shape
.
copy
())
if
len
(
self
.
shapesBackups
)
>=
10
:
self
.
shapesBackups
=
self
.
shapesBackups
[
-
9
:]
self
.
shapesBackups
.
append
(
shapesBackup
)
# 每删除或保存一次都会backup一次
def
copySelectedShape
(
self
):
if
self
.
selectedShape
:
shape
=
self
.
selectedShape
.
copy
()
self
.
deSelectShape
()
self
.
shapes
.
append
(
shape
)
shape
.
selected
=
True
self
.
selectedShape
=
shape
self
.
boundedShiftShape
(
shape
)
return
shape
# if self.selectedShape:
# shape = self.selectedShape.copy()
# self.deSelectShape()
# self.shapes.append(shape)
# shape.selected = True
# self.selectedShape = shape
# self.boundedShiftShape(shape)
# return shape
if
self
.
selectedShapes
:
self
.
selectedShapesCopy
=
[
s
.
copy
()
for
s
in
self
.
selectedShapes
]
self
.
boundedShiftShapes
(
self
.
selectedShapesCopy
)
self
.
endMove
(
copy
=
True
)
return
self
.
selectedShapes
def
boundedShiftShape
(
self
,
shape
):
# Try to move in one direction, and if it fails in another.
...
...
@@ -560,8 +671,9 @@ class Canvas(QWidget):
if
self
.
current
:
self
.
current
.
paint
(
p
)
self
.
line
.
paint
(
p
)
if
self
.
selectedShapeCopy
:
self
.
selectedShapeCopy
.
paint
(
p
)
if
self
.
selectedShapesCopy
:
for
s
in
self
.
selectedShapesCopy
:
s
.
paint
(
p
)
# Paint rect
if
self
.
current
is
not
None
and
len
(
self
.
line
)
==
2
and
not
self
.
fourpoint
:
...
...
@@ -689,14 +801,14 @@ class Canvas(QWidget):
self
.
update
()
elif
key
==
Qt
.
Key_Return
and
self
.
canCloseShape
():
self
.
finalise
()
elif
key
==
Qt
.
Key_Left
and
self
.
selectedShape
:
self
.
moveOnePixel
(
'Left'
)
elif
key
==
Qt
.
Key_Right
and
self
.
selectedShape
:
self
.
moveOnePixel
(
'Right'
)
elif
key
==
Qt
.
Key_Up
and
self
.
selectedShape
:
self
.
moveOnePixel
(
'Up'
)
elif
key
==
Qt
.
Key_Down
and
self
.
selectedShape
:
self
.
moveOnePixel
(
'Down'
)
#
elif key == Qt.Key_Left and self.selectedShape:
#
self.moveOnePixel('Left')
#
elif key == Qt.Key_Right and self.selectedShape:
#
self.moveOnePixel('Right')
#
elif key == Qt.Key_Up and self.selectedShape:
#
self.moveOnePixel('Up')
#
elif key == Qt.Key_Down and self.selectedShape:
#
self.moveOnePixel('Down')
def
moveOnePixel
(
self
,
direction
):
# print(self.selectedShape.points)
...
...
@@ -739,6 +851,8 @@ class Canvas(QWidget):
if
fill_color
:
self
.
shapes
[
-
1
].
fill_color
=
fill_color
#self.shapesBackups.pop() # 新建shape后要pop?
self
.
storeShapes
()
return
self
.
shapes
[
-
1
]
...
...
@@ -749,6 +863,17 @@ class Canvas(QWidget):
self
.
line
.
points
=
[
self
.
current
[
-
1
],
self
.
current
[
0
]]
self
.
drawingPolygon
.
emit
(
True
)
def
undoLastPoint
(
self
):
if
not
self
.
current
or
self
.
current
.
isClosed
():
return
self
.
current
.
popPoint
()
if
len
(
self
.
current
)
>
0
:
self
.
line
[
0
]
=
self
.
current
[
-
1
]
else
:
self
.
current
=
None
self
.
drawingPolygon
.
emit
(
False
)
self
.
repaint
()
def
resetAllLines
(
self
):
assert
self
.
shapes
self
.
current
=
self
.
shapes
.
pop
()
...
...
@@ -762,11 +887,18 @@ class Canvas(QWidget):
def
loadPixmap
(
self
,
pixmap
):
self
.
pixmap
=
pixmap
self
.
shapes
=
[]
self
.
repaint
()
# 这函数在哪
self
.
repaint
()
def
loadShapes
(
self
,
shapes
):
self
.
shapes
=
list
(
shapes
)
def
loadShapes
(
self
,
shapes
,
replace
=
True
):
if
replace
:
self
.
shapes
=
list
(
shapes
)
else
:
self
.
shapes
.
extend
(
shapes
)
self
.
current
=
None
self
.
hShape
=
None
self
.
hVertex
=
None
# self.hEdge = None
self
.
storeShapes
()
self
.
repaint
()
def
setShapeVisible
(
self
,
shape
,
value
):
...
...
@@ -793,6 +925,25 @@ class Canvas(QWidget):
self
.
restoreCursor
()
self
.
pixmap
=
None
self
.
update
()
self
.
shapesBackups
=
[]
def
setDrawingShapeToSquare
(
self
,
status
):
self
.
drawSquare
=
status
def
restoreShape
(
self
):
# 用于撤销
if
not
self
.
isShapeRestorable
:
return
self
.
shapesBackups
.
pop
()
# latest
shapesBackup
=
self
.
shapesBackups
.
pop
()
self
.
shapes
=
shapesBackup
#self.shapes.append(self.shapesBackups.pop()) # 为何这里之前是只将back赋值呢
self
.
selectedShapes
=
[]
for
shape
in
self
.
shapes
:
shape
.
selected
=
False
self
.
repaint
()
@
property
def
isShapeRestorable
(
self
):
if
len
(
self
.
shapesBackups
)
<
2
:
return
False
return
True
\ No newline at end of file
PPOCRLabel/libs/resources.py
浏览文件 @
ecf8b569
因为 它太大了无法显示 source diff 。你可以改为
查看blob
。
PPOCRLabel/libs/shape.py
浏览文件 @
ecf8b569
...
...
@@ -82,8 +82,11 @@ class Shape(object):
return
False
def
addPoint
(
self
,
point
):
if
not
self
.
reachMaxPoints
():
self
.
points
.
append
(
point
)
if
not
self
.
reachMaxPoints
():
# 4个点时发出close信号
if
self
.
points
and
point
==
self
.
points
[
0
]:
self
.
close
()
else
:
self
.
points
.
append
(
point
)
def
popPoint
(
self
):
if
self
.
points
:
...
...
PPOCRLabel/resources/strings/strings-zh-CN.properties
浏览文件 @
ecf8b569
...
...
@@ -96,4 +96,7 @@ hideBox=隐藏所有标注
showBox
=
显示所有标注
saveLabel
=
保存标记结果
singleRe
=
重识别此区块
labelDialogOption
=
弹出标记输入框
\ No newline at end of file
labelDialogOption
=
弹出标记输入框
undo
=
撤销
undoLastPoint
=
撤销上个点
autoSaveMode
=
自动保存标记结果
\ No newline at end of file
PPOCRLabel/resources/strings/strings.properties
浏览文件 @
ecf8b569
...
...
@@ -96,4 +96,7 @@ hideBox=Hide All Box
showBox
=
Show All Box
saveLabel
=
Save Label
singleRe
=
Re-recognition RectBox
labelDialogOption
=
Pop-up Label Input Dialog
\ No newline at end of file
labelDialogOption
=
Pop-up Label Input Dialog
undo
=
Undo
undoLastPoint
=
Undo Last Point
autoSaveMode
=
Auto Save Label Mode
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录