개정판 690353f6
issue #563 add LineNoTracerRunItem and fix run sort and add drawing flat function
Change-Id: I2e9667acf7aa10c28c922b9eeb6fe15b1a71db12
DTI_PID/DTI_PID/ConfigurationDialog.py | ||
---|---|---|
85 | 85 |
self.ui.spinBoxMinimumSize.setValue(int(configs[0].value)) if 1 == len(configs) else self.ui.spinBoxMinimumSize.setValue(30) |
86 | 86 |
configs = docData.getConfigs('Filter', 'DilateSize') |
87 | 87 |
self.ui.spinBoxDilateSize.setValue(int(configs[0].value)) if 1 == len(configs) else self.ui.spinBoxDilateSize.setValue(0) |
88 |
configs = docData.getConfigs('Filter', 'FlatSize') |
|
89 |
self.ui.spinBoxFlatSize.setValue(int(configs[0].value)) if 1 == len(configs) else self.ui.spinBoxFlatSize.setValue(0) |
|
88 | 90 |
|
89 | 91 |
# set min,max area for small object |
90 | 92 |
configs = docData.getConfigs('Small Object Size', 'Min Area') |
... | ... | |
662 | 664 |
configs.append(Config('Flow Mark', 'Length', self.ui.spinBoxFlowMarkLength.value())) |
663 | 665 |
configs.append(Config('Filter', 'MinimumSize', self.ui.spinBoxMinimumSize.value())) |
664 | 666 |
configs.append(Config('Filter', 'DilateSize', self.ui.spinBoxDilateSize.value())) |
667 |
configs.append(Config('Filter', 'FlatSize', self.ui.spinBoxFlatSize.value())) |
|
665 | 668 |
configs.append(Config('Small Object Size', 'Min Area', self.ui.spinBoxMinArea.value())) |
666 | 669 |
configs.append(Config('Small Object Size', 'Max Area', self.ui.spinBoxMaxArea.value())) |
667 | 670 |
configs.append(Config('Sliding Window', 'Width', self.ui.spinBoxWidth.value())) |
DTI_PID/DTI_PID/Configuration_UI.py | ||
---|---|---|
276 | 276 |
self.horizontalLayout_16 = QtWidgets.QHBoxLayout() |
277 | 277 |
self.horizontalLayout_16.setObjectName("horizontalLayout_16") |
278 | 278 |
self.label_10 = QtWidgets.QLabel(self.groupBoxFilter) |
279 |
self.label_10.setMaximumSize(QtCore.QSize(155, 16777215))
|
|
279 |
self.label_10.setMaximumSize(QtCore.QSize(230, 16777215))
|
|
280 | 280 |
self.label_10.setObjectName("label_10") |
281 | 281 |
self.horizontalLayout_16.addWidget(self.label_10, 0, QtCore.Qt.AlignVCenter) |
282 | 282 |
self.spinBoxMinimumSize = QtWidgets.QSpinBox(self.groupBoxFilter) |
... | ... | |
286 | 286 |
self.horizontalLayout_6 = QtWidgets.QHBoxLayout() |
287 | 287 |
self.horizontalLayout_6.setObjectName("horizontalLayout_6") |
288 | 288 |
self.label_29 = QtWidgets.QLabel(self.groupBoxFilter) |
289 |
self.label_29.setMaximumSize(QtCore.QSize(155, 16777215))
|
|
289 |
self.label_29.setMaximumSize(QtCore.QSize(230, 16777215))
|
|
290 | 290 |
self.label_29.setObjectName("label_29") |
291 | 291 |
self.horizontalLayout_6.addWidget(self.label_29) |
292 | 292 |
self.spinBoxDilateSize = QtWidgets.QSpinBox(self.groupBoxFilter) |
... | ... | |
294 | 294 |
self.spinBoxDilateSize.setObjectName("spinBoxDilateSize") |
295 | 295 |
self.horizontalLayout_6.addWidget(self.spinBoxDilateSize) |
296 | 296 |
self.verticalLayout_9.addLayout(self.horizontalLayout_6) |
297 |
self.horizontalLayout_10 = QtWidgets.QHBoxLayout() |
|
298 |
self.horizontalLayout_10.setObjectName("horizontalLayout_10") |
|
299 |
self.label_32 = QtWidgets.QLabel(self.groupBoxFilter) |
|
300 |
self.label_32.setMaximumSize(QtCore.QSize(230, 16777215)) |
|
301 |
self.label_32.setObjectName("label_32") |
|
302 |
self.horizontalLayout_10.addWidget(self.label_32) |
|
303 |
self.spinBoxFlatSize = QtWidgets.QSpinBox(self.groupBoxFilter) |
|
304 |
self.spinBoxFlatSize.setObjectName("spinBoxFlatSize") |
|
305 |
self.horizontalLayout_10.addWidget(self.spinBoxFlatSize) |
|
306 |
self.verticalLayout_9.addLayout(self.horizontalLayout_10) |
|
297 | 307 |
self.gridLayout_8.addLayout(self.verticalLayout_9, 0, 0, 1, 1) |
298 | 308 |
self.gridLayout_2.addWidget(self.groupBoxFilter, 2, 1, 1, 1) |
299 | 309 |
self.tabWidget.addTab(self.Recognition, "") |
... | ... | |
602 | 612 |
self.label_5.setText(_translate("ConfigurationDialog", "< Area < ")) |
603 | 613 |
self.label_4.setText(_translate("ConfigurationDialog", "Ignore Small Object Size : ")) |
604 | 614 |
self.label_9.setText(_translate("ConfigurationDialog", "Length to Connect Line")) |
605 |
self.label_16.setText(_translate("ConfigurationDialog", "Small Line Minimum Length"))
|
|
615 |
self.label_16.setText(_translate("ConfigurationDialog", "Line Minimum Length")) |
|
606 | 616 |
self.label.setText(_translate("ConfigurationDialog", "Sliding Window Size(WxH) : ")) |
607 | 617 |
self.label_28.setText(_translate("ConfigurationDialog", "Default Line Type")) |
608 | 618 |
self.groupBoxLineNo.setTitle(_translate("ConfigurationDialog", "Line No")) |
... | ... | |
619 | 629 |
self.label_22.setText(_translate("ConfigurationDialog", "Merge Size : ")) |
620 | 630 |
self.groupBoxFilter.setTitle(_translate("ConfigurationDialog", "Filter")) |
621 | 631 |
self.label_10.setText(_translate("ConfigurationDialog", "Minimum Detection Size : ")) |
622 |
self.label_29.setText(_translate("ConfigurationDialog", "Drawing Dilate Size : ")) |
|
632 |
self.label_29.setText(_translate("ConfigurationDialog", "Drawing Thickness Reinforcement Size : ")) |
|
633 |
self.label_32.setText(_translate("ConfigurationDialog", "Drawing Flattening Size : ")) |
|
623 | 634 |
self.tabWidget.setTabText(self.tabWidget.indexOf(self.Recognition), _translate("ConfigurationDialog", "Recognition")) |
624 | 635 |
self.groupBox_4.setTitle(_translate("ConfigurationDialog", "Note No Tag Rule")) |
625 | 636 |
self.checkBoxNoteNoSymbolName.setText(_translate("ConfigurationDialog", "Note No Symbol Name : ")) |
DTI_PID/DTI_PID/LineNoTracer.py | ||
---|---|---|
403 | 403 |
from EngineeringErrorItem import QEngineeringErrorItem |
404 | 404 |
from EngineeringTextItem import QEngineeringTextItem |
405 | 405 |
from SpecialItemTypesDialog import SpecialItemTracer |
406 |
from EngineeringLineTracerRunItem import QEngineeringLineTracerRunItem |
|
406 | 407 |
|
407 | 408 |
try: |
408 | 409 |
worker.displayMessage.emit('Initiating...') |
... | ... | |
712 | 713 |
lineNo.update_flow_mark(position, length) |
713 | 714 |
|
714 | 715 |
# sort run flow order |
715 |
fixed_run_infos = [] |
|
716 |
waiting_run_infos = [] |
|
716 |
fixed_run_infos = [] # QEngineeringLineTracerRunItem s
|
|
717 |
waiting_run_infos = [] # QEngineeringLineTracerRunItem s
|
|
717 | 718 |
runs = [] |
718 | 719 |
# first step : make fixed run using symbol info |
719 | 720 |
for lineNo in [lineNo for lineNo in docdata.tracerLineNos if len(lineNo.runs) > 0]: |
... | ... | |
724 | 725 |
|
725 | 726 |
# if from and to info already was entered, fix run flow |
726 | 727 |
if hasattr(lineNo, '_fixed') and lineNo._fixed and not_secondary: |
727 |
fixed_run_infos.append([run, lineNo, not_trim, not_secondary])
|
|
728 |
fixed_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
|
|
728 | 729 |
continue |
729 | 730 |
|
730 | 731 |
reference_symbols = [item for item in run.items if issubclass(type(item), SymbolSvgItem) and item.has_in_out_connector()] |
... | ... | |
735 | 736 |
if reference_symbol is run.items[0]: |
736 | 737 |
if len([connector_index for connector_index in reference_symbol.in_out_connector[0] \ |
737 | 738 |
if reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0: |
738 |
if not_trim and not_secondary: |
|
739 |
lineNo.reverse() |
|
740 |
else: |
|
741 |
run.reverse() |
|
742 |
fixed_run_infos.append([run, lineNo, not_trim, not_secondary]) |
|
739 |
info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary) |
|
740 |
info.reverse() |
|
741 |
fixed_run_infos.append(info) |
|
743 | 742 |
break |
744 | 743 |
elif len([connector_index for connector_index in reference_symbol.in_out_connector[1] \ |
745 | 744 |
if reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0: |
746 |
fixed_run_infos.append([run, lineNo, not_trim, not_secondary]) |
|
745 |
info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary) |
|
746 |
fixed_run_infos.append(info) |
|
747 | 747 |
break |
748 | 748 |
# place at last |
749 | 749 |
elif reference_symbol is run.items[-1]: |
750 | 750 |
if len([connector_index for connector_index in reference_symbol.in_out_connector[1] \ |
751 | 751 |
if reference_symbol.connectors[connector_index].connectedItem is run.items[-2]]) > 0: |
752 |
if not_trim and not_secondary: |
|
753 |
lineNo.reverse() |
|
754 |
else: |
|
755 |
run.reverse() |
|
756 |
fixed_run_infos.append([run, lineNo, not_trim, not_secondary]) |
|
752 |
info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary) |
|
753 |
info.reverse() |
|
754 |
fixed_run_infos.append(info) |
|
757 | 755 |
break |
758 | 756 |
elif len([connector_index for connector_index in reference_symbol.in_out_connector[0] \ |
759 | 757 |
if reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0: |
760 |
fixed_run_infos.append([run, lineNo, not_trim, not_secondary]) |
|
758 |
info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary) |
|
759 |
fixed_run_infos.append(info) |
|
761 | 760 |
break |
762 | 761 |
# place at middle |
763 | 762 |
else: |
... | ... | |
771 | 770 |
in_item_index = run.items.index(in_item) |
772 | 771 |
out_item_index = run.items.index(out_item) |
773 | 772 |
if out_item_index < in_item_index: |
774 |
if not_trim and not_secondary: |
|
775 |
lineNo.reverse() |
|
776 |
else: |
|
777 |
run.reverse() |
|
778 |
fixed_run_infos.append([run, lineNo, not_trim, not_secondary]) |
|
773 |
info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary) |
|
774 |
info.reverse() |
|
775 |
fixed_run_infos.append(info) |
|
776 |
break |
|
777 |
else: |
|
778 |
info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary) |
|
779 |
fixed_run_infos.append(info) |
|
779 | 780 |
break |
780 |
waiting_run_infos.append([run, lineNo, not_trim, not_secondary]) |
|
781 | 781 |
# only symbol runs doesn't need flow |
782 | 782 |
elif len(run.items) is 1 and issubclass(type(run.items[0]), SymbolSvgItem): |
783 |
runs.append([run, lineNo, not_trim, not_secondary])
|
|
783 |
runs.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
|
|
784 | 784 |
# runs can't know flow directly |
785 | 785 |
else: |
786 |
waiting_run_infos.append([run, lineNo, not_trim, not_secondary])
|
|
786 |
waiting_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
|
|
787 | 787 |
run_index += 1 |
788 | 788 |
|
789 |
# second step : determine waiting run flow |
|
789 |
# second step : determine waiting run flow, connected by point
|
|
790 | 790 |
remain_count_past = len(waiting_run_infos) |
791 | 791 |
while True: |
792 | 792 |
remain_count = 0 |
793 | 793 |
for run_index in reversed(range(len(waiting_run_infos))): |
794 | 794 |
waiting_run_info = waiting_run_infos[run_index] |
795 |
waiting_run = waiting_run_info[0]
|
|
795 |
waiting_run = waiting_run_info.run
|
|
796 | 796 |
for fixed_run_info in fixed_run_infos: |
797 |
fixed_run = fixed_run_info[0]
|
|
797 |
fixed_run = fixed_run_info.run
|
|
798 | 798 |
if len(waiting_run.items) > 1 and len(fixed_run.items) > 1: |
799 | 799 |
if waiting_run.items[0].is_connected(fixed_run.items[0]): |
800 |
if waiting_run_info[2] and waiting_run_info[3]: |
|
801 |
waiting_run_info[1].reverse() |
|
802 |
else: |
|
803 |
run.reverse() |
|
800 |
waiting_run_info.reverse() |
|
804 | 801 |
fixed_run_infos.append(waiting_run_info) |
805 | 802 |
waiting_run_infos.pop(run_index) |
806 | 803 |
break |
... | ... | |
809 | 806 |
waiting_run_infos.pop(run_index) |
810 | 807 |
break |
811 | 808 |
elif waiting_run.items[-1].is_connected(fixed_run.items[-1]): |
812 |
if waiting_run_info[2] and waiting_run_info[3]: |
|
813 |
waiting_run_info[1].reverse() |
|
814 |
else: |
|
815 |
run.reverse() |
|
809 |
waiting_run_info.reverse() |
|
816 | 810 |
fixed_run_infos.append(waiting_run_info) |
817 | 811 |
waiting_run_infos.pop(run_index) |
818 | 812 |
break |
... | ... | |
824 | 818 |
remain_count += 1 |
825 | 819 |
elif len(fixed_run.items) > 1: |
826 | 820 |
if waiting_run.items[0].connectors[0].connectedItem is fixed_run.items[0]: |
827 |
waiting_run.reverse() |
|
821 |
waiting_run_info.reverse()
|
|
828 | 822 |
fixed_run_infos.append(waiting_run_info) |
829 | 823 |
waiting_run_infos.pop(run_index) |
830 | 824 |
break |
... | ... | |
833 | 827 |
waiting_run_infos.pop(run_index) |
834 | 828 |
break |
835 | 829 |
elif waiting_run.items[0].connectors[1].connectedItem is fixed_run.items[-1]: |
836 |
waiting_run.reverse() |
|
830 |
waiting_run_info.reverse()
|
|
837 | 831 |
fixed_run_infos.append(waiting_run_info) |
838 | 832 |
waiting_run_infos.pop(run_index) |
839 | 833 |
break |
... | ... | |
869 | 863 |
else: |
870 | 864 |
remain_count_past = remain_count |
871 | 865 |
|
872 |
# third step : body connected run sort remain |
|
866 |
# third step : body connected run sort |
|
867 |
fixed_merged_run_infos = [] |
|
868 |
waiting_mergerd_run_infos = [] |
|
869 |
for waiting_run_info in waiting_run_infos: |
|
870 |
for waiting_run_info in waiting_run_infos: |
|
871 |
pass |
|
873 | 872 |
|
874 | 873 |
# trace special item |
875 | 874 |
worker.displayMessage.emit('Find line for special item...') |
DTI_PID/DTI_PID/MainWindow.py | ||
---|---|---|
2305 | 2305 |
|
2306 | 2306 |
imgNot = np.ones(imgDiff.shape, np.uint8) |
2307 | 2307 |
cv2.bitwise_not(imgDiff, imgNot) |
2308 |
imgNot = cv2.dilate(imgNot, np.ones((8, 8), np.uint8)) |
|
2308 |
imgNot = cv2.erode(imgNot, np.ones((2, 2), np.uint8)) |
|
2309 |
imgNot = cv2.dilate(imgNot, np.ones((10, 10), np.uint8)) |
|
2309 | 2310 |
|
2310 | 2311 |
contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
2311 | 2312 |
|
DTI_PID/DTI_PID/QtImageViewer.py | ||
---|---|---|
209 | 209 |
#blur = cv2.GaussianBlur(cvImg, (5,5),0) |
210 | 210 |
cvImg = cv2.threshold(cvImg, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1] |
211 | 211 |
|
212 |
# skeletonize |
|
213 |
''' |
|
214 |
from skimage import io |
|
215 |
from skimage import morphology |
|
216 |
|
|
217 |
image = io.imread(fileName) |
|
218 |
out = morphology.skeletonize(image > 0) |
|
219 |
io.imshow(out) |
|
220 |
cvImg = out[:, :, ::-1] |
|
221 |
cv2.namedWindow('original', cv2.WINDOW_NORMAL) |
|
222 |
cv2.imshow('original',cvImg) |
|
223 |
''' |
|
224 |
''' |
|
225 |
cvImg = ~cvImg |
|
226 |
|
|
227 |
size = np.size(cvImg) |
|
228 |
skel = np.zeros(cvImg.shape,np.uint8) |
|
229 |
|
|
230 |
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3)) |
|
231 |
done = False |
|
232 |
|
|
233 |
while( not done): |
|
234 |
eroded = cv2.erode(cvImg,element) |
|
235 |
temp = cv2.dilate(eroded,element) |
|
236 |
temp = cv2.subtract(cvImg,temp) |
|
237 |
skel = cv2.bitwise_or(skel,temp) |
|
238 |
cvImg = eroded.copy() |
|
239 |
|
|
240 |
zeros = size - cv2.countNonZero(cvImg) |
|
241 |
if zeros==size: |
|
242 |
done = True |
|
243 |
cvImg = ~skel |
|
244 |
''' |
|
245 |
|
|
212 | 246 |
configs = AppDocData.instance().getConfigs('Filter', 'DilateSize') |
213 | 247 |
if 1 == len(configs) and int(configs[0].value) is not 0: |
214 | 248 |
size = int(configs[0].value) |
215 | 249 |
kernel = np.ones((size, size), np.uint8) |
216 | 250 |
cvImg = cv2.erode(cvImg, kernel, iterations=1) |
217 | 251 |
|
252 |
configs = AppDocData.instance().getConfigs('Filter', 'FlatSize') |
|
253 |
if 1 == len(configs) and int(configs[0].value) is not 0: |
|
254 |
size = int(configs[0].value) |
|
255 |
kernel = np.ones((size, size), np.uint8) |
|
256 |
cvImg = cv2.morphologyEx(cvImg, cv2.MORPH_CLOSE, kernel) |
|
257 |
cvImg = cv2.morphologyEx(cvImg, cv2.MORPH_OPEN, kernel) |
|
258 |
|
|
218 | 259 |
bytesPerLine = cvImg.shape[1] |
219 | 260 |
image = QImage(cvImg.data, cvImg.shape[1], cvImg.shape[0], bytesPerLine, QImage.Format_Indexed8) |
220 | 261 |
self.setImage(image) |
DTI_PID/DTI_PID/Shapes/EngineeringLineTracerRunItem.py | ||
---|---|---|
1 |
# coding: utf-8 |
|
2 |
""" |
|
3 |
This is engineering run item module for sort line nos and runs |
|
4 |
""" |
|
5 |
|
|
6 |
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem |
|
7 |
from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem |
|
8 |
from AppDocData import AppDocData |
|
9 |
|
|
10 |
class QEngineeringLineTracerRunItem(): |
|
11 |
""" |
|
12 |
This is engineering sort run item class |
|
13 |
""" |
|
14 |
|
|
15 |
def __init__(self, run, lineNo=None, not_trim=None, not_secondary=None, consumed=False): |
|
16 |
self.run = run |
|
17 |
self.lineNo = lineNo if lineNo else self.run.onwer |
|
18 |
|
|
19 |
if not_trim: |
|
20 |
self.not_trim = not_trim |
|
21 |
else: |
|
22 |
self.not_trim = True if type(lineNo) is QEngineeringLineNoTextItem else False |
|
23 |
|
|
24 |
if not_secondary: |
|
25 |
self.not_secondary = not_secondary |
|
26 |
else: |
|
27 |
self.not_secondary = True if self.run is self.lineNo.runs[0] else False |
|
28 |
|
|
29 |
self.consumed = consumed |
|
30 |
|
|
31 |
def reverse(self): |
|
32 |
if self.not_trim and self.not_secondary: |
|
33 |
self.lineNo.reverse() |
|
34 |
else: |
|
35 |
self.run.reverse() |
DTI_PID/DTI_PID/Shapes/QEngineeringTrimLineNoTextItem.py | ||
---|---|---|
28 | 28 |
This is engineering trim line no text item class |
29 | 29 |
""" |
30 | 30 |
|
31 |
''' |
|
32 |
''' |
|
33 | 31 |
def __init__(self, parent=None): |
34 | 32 |
import uuid |
35 | 33 |
|
DTI_PID/DTI_PID/UI/Configuration.ui | ||
---|---|---|
232 | 232 |
<item row="2" column="0"> |
233 | 233 |
<widget class="QLabel" name="label_16"> |
234 | 234 |
<property name="text"> |
235 |
<string>Small Line Minimum Length</string>
|
|
235 |
<string>Line Minimum Length</string> |
|
236 | 236 |
</property> |
237 | 237 |
</widget> |
238 | 238 |
</item> |
... | ... | |
613 | 613 |
<widget class="QLabel" name="label_10"> |
614 | 614 |
<property name="maximumSize"> |
615 | 615 |
<size> |
616 |
<width>155</width>
|
|
616 |
<width>230</width>
|
|
617 | 617 |
<height>16777215</height> |
618 | 618 |
</size> |
619 | 619 |
</property> |
... | ... | |
633 | 633 |
<widget class="QLabel" name="label_29"> |
634 | 634 |
<property name="maximumSize"> |
635 | 635 |
<size> |
636 |
<width>155</width>
|
|
636 |
<width>230</width>
|
|
637 | 637 |
<height>16777215</height> |
638 | 638 |
</size> |
639 | 639 |
</property> |
640 | 640 |
<property name="text"> |
641 |
<string>Drawing Dilate Size : </string>
|
|
641 |
<string>Drawing Thickness Reinforcement Size : </string>
|
|
642 | 642 |
</property> |
643 | 643 |
</widget> |
644 | 644 |
</item> |
... | ... | |
651 | 651 |
</item> |
652 | 652 |
</layout> |
653 | 653 |
</item> |
654 |
<item> |
|
655 |
<layout class="QHBoxLayout" name="horizontalLayout_10"> |
|
656 |
<item> |
|
657 |
<widget class="QLabel" name="label_32"> |
|
658 |
<property name="maximumSize"> |
|
659 |
<size> |
|
660 |
<width>230</width> |
|
661 |
<height>16777215</height> |
|
662 |
</size> |
|
663 |
</property> |
|
664 |
<property name="text"> |
|
665 |
<string>Drawing Flattening Size : </string> |
|
666 |
</property> |
|
667 |
</widget> |
|
668 |
</item> |
|
669 |
<item> |
|
670 |
<widget class="QSpinBox" name="spinBoxFlatSize"/> |
|
671 |
</item> |
|
672 |
</layout> |
|
673 |
</item> |
|
654 | 674 |
</layout> |
655 | 675 |
</item> |
656 | 676 |
</layout> |
내보내기 Unified diff