프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

hytos / DTI_PID / DTI_PID / ImportTextFromCADDialog.py @ e02478ba

이력 | 보기 | 이력해설 | 다운로드 (75.4 KB)

1
# coding: utf-8
2
"""This is text importing module from AutoCAD"""
3
import math
4
import os
5
import sys
6
from PyQt5.QtCore import *
7
from PyQt5.QtGui import *
8
from PyQt5.QtWidgets import *
9
from AppDocData import AppDocData
10
from functools import reduce, partial
11
import subprocess
12
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree, parse
13
from SymbolRegiDataListDialog import QSymbolRegiDataListDialog
14
import uuid
15
from Drawing import Drawing
16

    
17
import ImportTextFromCAD_UI
18

    
19

    
20
class LineTypeMappingDelegate(QStyledItemDelegate):
21
    def __init__(self, layer_names: list, parent=None):
22
        QStyledItemDelegate.__init__(self, parent)
23

    
24
        self._layer_names = layer_names
25

    
26
    def createEditor(self, parent, option, index):
27
        editor = None
28

    
29
        item = index.model().itemFromIndex(index)
30
        if not item.parent():
31
            if index.column() == 1:
32
                editor = QComboBox(parent)
33
                editor.addItems(self._layer_names if self._layer_names else [''])
34

    
35
        return editor if editor else super(LineTypeMappingDelegate, self).createEditor(parent, option, index)
36

    
37

    
38
class SymbolMappingDelegate(QStyledItemDelegate):
39
    def __init__(self, symbol_types: list, parent=None):
40
        QStyledItemDelegate.__init__(self, parent)
41

    
42
        self._symbol_types = symbol_types
43

    
44
    def createEditor(self, parent, option, index):
45
        """create a editor for symbol mapping"""
46
        editor = None
47

    
48
        item = index.model().itemFromIndex(index)
49
        if item.parent() and not item.parent().parent():
50
            if index.column() == 1:
51
                editor = QComboBox(parent)
52
                editor.addItems(self._symbol_types if self._symbol_types else [''])
53

    
54
        return editor if editor else super(SymbolMappingDelegate, self).createEditor(parent, option, index)
55

    
56

    
57
class ExcludeLayersDelegate(QStyledItemDelegate):
58
    def editorEvent(self, event, model, option, index):
59
        checked = index.data(Qt.CheckStateRole)
60
        ret = QStyledItemDelegate.editorEvent(self, event, model, option, index)
61
        if checked != index.data(Qt.CheckStateRole):
62
            self.parent().checked.emit(index)
63

    
64
        return ret
65

    
66

    
67
class LineTypeMappingModel(QStandardItemModel):
68
    """This is LineTypeMapping Model class"""
69
    def __init__(self):
70
        from LineTypeConditions import LineTypeConditions
71

    
72
        QStandardItemModel.__init__(self)
73

    
74
        app_doc_data = AppDocData.instance()
75
        configs = app_doc_data.getConfigs(section='Line Type Mapping')
76

    
77
        for condition in LineTypeConditions.items():
78
            items = [QStandardItem(condition.name), QStandardItem(''), QStandardItem('')]
79
            items[0].setEditable(False)
80
            self.appendRow(items)
81

    
82
            matches = [config for config in configs if config.key == condition.name]
83
            for match in matches:
84
                line_types = match.value.split(',')
85
                for line_type in line_types:
86
                    childs = [QStandardItem(''), QStandardItem(line_type), QStandardItem('')]
87
                    childs[0].setData(condition.name, Qt.UserRole)
88
                    for child in childs:
89
                        child.setEditable(False)
90
                    items[0].appendRow(childs)
91

    
92
        headers = [QStandardItem("ID2 Line Type"), QStandardItem("AutoCAD Line Layer"), QStandardItem('')]
93
        for idx, header in enumerate(headers):
94
            header.setTextAlignment(Qt.AlignCenter)
95
            self.setHorizontalHeaderItem(idx, header)
96

    
97

    
98
class SymbolMappingModel(QStandardItemModel):
99
    """This is SymbolMapping Model class"""
100
    def __init__(self):
101
        """constructor"""
102
        QStandardItemModel.__init__(self)
103

    
104
        self._autocad_symbols = []  # holder for autocad symbol
105

    
106
        project = AppDocData.instance().getCurrentProject()
107
        if project is not None:
108
            self.clear()
109
            self.load_symbol_info()
110

    
111
            # load and display symbol mapping
112
            app_doc_data = AppDocData.instance()
113
            configs = app_doc_data.getConfigs(section='Symbol Mapping')
114

    
115
            for row in range(self.rowCount()):
116
                category_index = self.index(row, 0)
117

    
118
                child_count = self.rowCount(category_index)
119
                for child_row in range(child_count):
120
                    child_index = self.index(child_row, 0, category_index)
121
                    id2_symbol_item = self.itemFromIndex(child_index)
122
                    id2_symbol_uid = id2_symbol_item.data(Qt.UserRole)
123

    
124
                    matches = [config for config in configs if config.key == str(id2_symbol_uid)]
125
                    for match in matches:
126
                        symbols = match.value.split(',')
127
                        for symbol in symbols:
128
                            if symbol not in self._autocad_symbols:
129
                                self._autocad_symbols.append(symbol)
130

    
131
                            childs = [QStandardItem(''), QStandardItem(symbol), QStandardItem('')]
132
                            childs[0].setData(id2_symbol_uid, Qt.UserRole)
133
                            for child in childs:
134
                                child.setEditable(False)
135
                            id2_symbol_item.appendRow(childs)
136

    
137
            headers = [QStandardItem("ID2 Symbol"), QStandardItem("AutoCAD Symbol"), QStandardItem('')]
138
            for idx, header in enumerate(headers):
139
                header.setTextAlignment(Qt.AlignCenter)
140
                self.setHorizontalHeaderItem(idx, header)
141

    
142
    def load_symbol_info(self):
143
        """load symbol information and display it on tree view"""
144
        try:
145
            app_doc_data = AppDocData.instance()
146

    
147
            symbolTypeList = app_doc_data.getSymbolTypeList()
148
            for row, symbolType in enumerate(symbolTypeList):
149
                items = [QStandardItem(symbolType[2]), QStandardItem(''), QStandardItem('')]
150
                items[0].setData(symbolType, Qt.UserRole)
151
                items[0].setEditable(False)
152
                items[0].setSelectable(False)
153
                items[1].setEditable(False)
154
                items[1].setSelectable(False)
155
                items[2].setEditable(False)
156
                items[2].setSelectable(False)
157

    
158
                symbolList = app_doc_data.getSymbolListByType('UID', symbolType[0])
159
                for symbol in symbolList:
160
                    childs = [QStandardItem(symbol.getName()), QStandardItem(''), QStandardItem('')]
161
                    childs[0].setData(symbol.getUid(), Qt.UserRole)
162
                    childs[0].setEditable(False)
163

    
164
                    _, svg = app_doc_data.read_symbol_shape(symbol.sName)
165
                    if svg:
166
                        symbol.pixmap = QPixmap()
167
                        symbol.pixmap.loadFromData(svg if isinstance(svg, bytes) else svg.encode())
168
                        icon = QIcon(symbol.pixmap)
169
                        childs[0].setIcon(icon)
170
                        childs[0].svgFilePath = None  # save svg file path
171
                    else:
172
                        svgPath = symbol.getSvgFileFullPath()
173
                        symbol.pixmap = QPixmap(svgPath)
174
                        icon = QIcon(symbol.pixmap)
175
                        childs[0].setIcon(icon)
176
                        childs[0].svgFilePath = svgPath  # save svg file path
177

    
178
                    items[0].appendRow(childs)
179

    
180
                items[0].sortChildren(0, Qt.AscendingOrder)
181
                self.appendRow(items)
182
        except Exception as ex:
183
            from App import App
184
            from AppDocData import MessageType
185

    
186
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
187
                      f'{sys.exc_info()[-1].tb_lineno}'
188
            App.mainWnd().addMessage.emit(MessageType.Error, message)
189

    
190
    @property
191
    def autocad_symbols(self):
192
        """property of autocad symbol"""
193
        return self._autocad_symbols
194

    
195

    
196
class ExcludeLayersModel(QStandardItemModel):
197
    def __init__(self, parent):
198
        """
199
        constructor
200
        :param parent:
201
        """
202
        import csv
203

    
204
        """constructor"""
205
        QStandardItemModel.__init__(self, parent=parent)
206

    
207
        self._autocad_symbols = []  # holder for autocad symbol
208

    
209
        project = AppDocData.instance().getCurrentProject()
210
        if project is not None:
211
            self.clear()
212

    
213
            # load and display exclude layers mapping
214
            app_doc_data = AppDocData.instance()
215
            configs = app_doc_data.getConfigs(section='Exclude Layers Mapping')
216
            if configs and configs[0]:
217
                values = list(csv.reader([configs[0].value], delimiter=',', escapechar='^'))[0]
218
                for value in values:
219
                    item = QStandardItem(value)
220
                    item.setCheckable(True)
221
                    item.setCheckState(Qt.Checked)
222
                    self.appendRow(item)
223

    
224
    def append_layers(self, layers: list) -> None:
225
        """
226
        add layers to listview
227
        :param layers:
228
        :return:
229
        """
230

    
231
        for layer in layers:
232
            items = self.findItems(layer)
233
            if not items:
234
                item = QStandardItem(layer)
235
                item.setCheckable(True)
236
                item.setCheckState(Qt.Unchecked)
237
                self.appendRow(item)
238

    
239

    
240
class QImportTextFromCADDialog(QDialog):
241
    def __init__(self, parent):
242
        """constructor"""
243
        QDialog.__init__(self, parent)
244

    
245
        self.ui = ImportTextFromCAD_UI.Ui_ImportTextFromCADDialog()
246
        self.ui.setupUi(self)
247

    
248
        self._dwgs = []
249
        self._layer_names, self._line_types = [], []
250
        self._symbol_types = []
251
        self.on_load_line_type_mapping()
252
        self.on_load_symbol_mapping()
253
        self.on_load_exclude_layers_mapping()
254

    
255
        app_doc_data = AppDocData.instance()
256
        #self.drawing_height = app_doc_data.activeDrawing.image.shape[1]
257

    
258
        configs = app_doc_data.getConfigs('Cad Offset', 'X')
259
        self.ui.spinBoxX.setValue(int(configs[0].value)) if 1 == len(configs) else \
260
                self.ui.spinBoxX.setValue(0)
261
        configs = app_doc_data.getConfigs('Cad Offset', 'Y')
262
        self.ui.spinBoxY.setValue(int(configs[0].value)) if 1 == len(configs) else \
263
                self.ui.spinBoxY.setValue(0)
264
        configs = app_doc_data.getConfigs('Cad Offset', 'Text X')
265
        self.ui.spinBoxTextX.setValue(int(configs[0].value)) if 1 == len(configs) else \
266
                self.ui.spinBoxTextX.setValue(0)
267
        configs = app_doc_data.getConfigs('Cad Offset', 'Text Y')
268
        self.ui.spinBoxTextY.setValue(int(configs[0].value)) if 1 == len(configs) else \
269
                self.ui.spinBoxTextY.setValue(0)        
270
        configs = app_doc_data.getConfigs('Cad Offset', 'Scale')
271
        self.ui.doubleSpinBoxScale.setValue(float(configs[0].value)) if 1 == len(configs) else \
272
                self.ui.doubleSpinBoxScale.setValue(0.5)
273
        configs = app_doc_data.getConfigs('Cad Offset', 'X Scale')
274
        self.ui.doubleSpinBox.setValue(float(configs[0].value)) if 1 == len(configs) else \
275
                self.ui.doubleSpinBoxScale.setValue(0.5)
276
        configs = app_doc_data.getConfigs('Cad Offset', 'Y Scale')
277
        self.ui.doubleSpinBox_2.setValue(float(configs[0].value)) if 1 == len(configs) else \
278
                self.ui.doubleSpinBoxScale.setValue(0.5)
279
        configs = app_doc_data.getConfigs('Cad State', 'Line')
280
        self.ui.checkBoxLine.setChecked(bool(int(configs[0].value))) if 1 == len(configs) else \
281
                self.ui.checkBoxLine.setChecked(False)
282
        configs = app_doc_data.getConfigs('Cad State', 'Text')
283
        self.ui.checkBoxText.setChecked(bool(int(configs[0].value))) if 1 == len(configs) else \
284
                self.ui.checkBoxText.setChecked(True)
285
        configs = app_doc_data.getConfigs('Cad State', 'Symbol')
286
        self.ui.checkBoxSymbol.setChecked(bool(int(configs[0].value))) if 1 == len(configs) else \
287
                self.ui.checkBoxSymbol.setChecked(False)
288

    
289
        self.text_scale = None
290
        self.id2_bbox = None
291
        self.scales = None
292
        self.offsets = None
293

    
294
        self.isAccepted = False
295
 
296
        self.ui.toolButtonCAD.clicked.connect(self.on_add_cad_click)
297
        self.ui.pushButtonSave.clicked.connect(self.on_save_mappings)
298
        self.ui.pushButtonImport.clicked.connect(self.importClicked)
299
        self.ui.pushButtonClose.clicked.connect(self.close)
300
        self.ui.pushButtonAuto.clicked.connect(self.autoCalOffset)
301
        self.ui.checkBoxAuto.stateChanged.connect(self.autoStateChanged)
302
        self.ui.checkBoxLegend.stateChanged.connect(self.legendStateChanged)
303
        self.ui.checkBoxAuto.stateChanged.connect(self.autoStateChanged)
304
        self.ui.pushButtonAutoMapping.clicked.connect(self.autoMapping)
305

    
306
        configs = app_doc_data.getConfigs('Cad State', 'Auto')
307
        self.ui.checkBoxAuto.setChecked(bool(int((configs[0].value)))) if 1 == len(configs) else \
308
                self.ui.checkBoxAuto.setChecked(True)
309

    
310
    def importClicked(self):
311
        if self.ui.checkBoxLegend.isChecked():
312
            self.on_import_legend()
313
        else:
314
            self.on_import_autocad()
315

    
316
    def legendStateChanged(self, checkState):
317
        if checkState is int(Qt.Checked):
318
            self.ui.checkBoxLine.setEnabled(False)
319
            self.ui.checkBoxSymbol.setEnabled(False)
320
            self.ui.checkBoxText.setEnabled(False)
321
        elif checkState is int(Qt.Unchecked):
322
            self.ui.checkBoxLine.setEnabled(True)
323
            self.ui.checkBoxSymbol.setEnabled(True)
324
            self.ui.checkBoxText.setEnabled(True)
325

    
326
    def autoStateChanged(self, checkState):
327
        if checkState is int(Qt.Checked):
328
            self.ui.spinBoxX.setEnabled(False)
329
            self.ui.spinBoxY.setEnabled(False)
330
            self.ui.spinBoxTextX.setEnabled(False)
331
            self.ui.spinBoxTextY.setEnabled(False)
332
            self.ui.spinBoxSymbolX.setEnabled(False)
333
            self.ui.spinBoxSymbolY.setEnabled(False)
334
            self.ui.doubleSpinBoxScale.setEnabled(False)
335
            self.ui.doubleSpinBox.setEnabled(False)
336
            self.ui.doubleSpinBox_2.setEnabled(False)
337
            self.ui.lineEdit.setEnabled(False)
338
            self.ui.lineEdit_2.setEnabled(False)
339
        elif checkState is int(Qt.Unchecked):
340
            self.ui.spinBoxX.setEnabled(True)
341
            self.ui.spinBoxY.setEnabled(True)
342
            self.ui.spinBoxTextX.setEnabled(True)
343
            self.ui.spinBoxTextY.setEnabled(True)
344
            self.ui.spinBoxSymbolX.setEnabled(True)
345
            self.ui.spinBoxSymbolY.setEnabled(True)
346
            self.ui.doubleSpinBoxScale.setEnabled(True)
347
            self.ui.doubleSpinBox.setEnabled(True)
348
            self.ui.doubleSpinBox_2.setEnabled(True)
349
            self.ui.lineEdit.setEnabled(True)
350
            self.ui.lineEdit_2.setEnabled(True)
351

    
352
    def autoMapping(self):
353
        if not self._dwgs:
354
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file(s)'))
355
            return
356

    
357
        model = self.ui.treeViewSymbolMapping.model()
358
        row_index = []
359
        for row in range(model.rowCount()):
360
            category_index = model.index(row, 0)
361

    
362
            child_count = model.rowCount(category_index)
363
            for child_row in range(child_count):
364
                child_index = model.index(child_row, 0, category_index)
365
                id2_symbol_item = model.itemFromIndex(child_index)
366
                id2_symbol_uid = id2_symbol_item.data(Qt.UserRole)
367
                cad_symbol_list = [(symbol if '+' not in symbol else symbol.split('+')[1], symbol) for symbol in self._symbol_types]
368
                matches = [cad_symbol for cad_symbol in cad_symbol_list if cad_symbol[0].upper() == id2_symbol_item.text().upper()]
369
                if matches:
370
                    row_index.insert(0, [row, child_row, matches[0][1], id2_symbol_uid, id2_symbol_item])
371

    
372
        for categoty, child, symbol, uid, item in row_index:
373
            category_index = model.index(categoty, 0)
374
            child_index = model.index(child, 0, category_index)
375
            if symbol not in model._autocad_symbols:
376
                model._autocad_symbols.append(symbol)
377
                childs = [QStandardItem(''), QStandardItem(symbol), QStandardItem('')]
378
                childs[0].setData(uid, Qt.UserRole)
379
                for _child in childs:
380
                    _child.setEditable(False)
381
                item.appendRow(childs)
382

    
383
                button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
384
                button.setMaximumWidth(20)
385
                _index = model.index(model.rowCount(child_index) - 1, 2, child_index)
386
                button.clicked.connect(self.on_remove_symbol_mapping_item)
387
                self.ui.treeViewSymbolMapping.setIndexWidget(_index, button)                    
388

    
389
    def autoCalOffset(self):
390
        """ auto calculate offset """
391
        from App import App
392
        from EngineeringTextItem import QEngineeringTextItem
393

    
394
        if not self._dwgs:
395
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file(s)'))
396
            return
397

    
398
        # find ID2 Text Item
399
        find_text_1 = self.ui.lineEdit.text()
400
        find_text_2 = self.ui.lineEdit_2.text()
401

    
402
        matches_1 = [item for item in App.mainWnd().graphicsView.items() if issubclass(type(item), QEngineeringTextItem) and item.text() == find_text_1]
403
        matches_2 = [item for item in App.mainWnd().graphicsView.items() if issubclass(type(item), QEngineeringTextItem) and item.text() == find_text_2]
404
        if not matches_1 or not matches_2:
405
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no search text items.'))
406
            return
407
        
408
        text_item_1 = matches_1[0]
409
        text_loc_1 = text_item_1.loc
410
        text_item_2 = matches_2[0]
411
        text_loc_2 = text_item_2.loc
412

    
413
        cad_item_loc_1 = None
414
        cad_item_loc_2 = None
415
        # up to here
416

    
417
        app_doc_data = AppDocData.instance()
418
        project = app_doc_data.getCurrentProject()
419

    
420
        temp_path = project.getTempPath()
421
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
422
                         (os.path.splitext(f)[1].upper() == '.XML')]
423

    
424
        _file = self._dwgs[0]
425
        file_name = os.path.splitext(os.path.basename(_file))[0]
426
        autocad_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
427
        matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
428

    
429
        if matches:
430
            try:
431
                id2_xml_path = os.path.join(temp_path, matches[0])
432
                id2_xml = parse(id2_xml_path)
433
                id2_xml_root = id2_xml.getroot()
434

    
435
                autocad_xml = parse(autocad_xml_path)
436
                autocad_xml_root = autocad_xml.getroot()
437
                
438
                find_1 = False
439
                find_2 = False
440
                for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
441
                    if find_1 and find_2:
442
                        break
443

    
444
                    if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
445
                        continue
446
                    '''
447
                    min_values = [float(token) for token in
448
                                    blk_tbl_record.attrib['MinExtents'].strip('(').strip(')').split(',')]
449
                    max_values = [float(token) for token in
450
                                    blk_tbl_record.attrib['MaxExtents'].strip('(').strip(')').split(',')]
451
                    autocad_bbox = [min_values[0], min_values[1],
452
                                    max_values[0] - min_values[0], max_values[1] - min_values[1]]
453
                    '''
454
                    for text_node in blk_tbl_record.iter('AcDbText'):
455
                        if find_text_1 != text_node.text and find_text_2 != text_node.text:
456
                            continue
457
                        elif find_text_1 == text_node.text:
458
                            x = float(text_node.attrib['X'])
459
                            y = float(text_node.attrib['Y'])
460
                            h = float(text_node.attrib['Height'])
461
                            cad_item_loc_1 = [x, y, h]
462
                            find_1 = True
463
                        elif find_text_2 == text_node.text:
464
                            x = float(text_node.attrib['X'])
465
                            y = float(text_node.attrib['Y'])
466
                            h = float(text_node.attrib['Height'])
467
                            cad_item_loc_2 = [x, y, h]
468
                            find_2 = True
469

    
470
                    if not find_1 or not find_2:
471
                        for blk_ref in blk_tbl_record.iter('AcDbBlockReference'):
472
                            if find_1 and find_2:
473
                                break
474

    
475
                            for text_node in blk_ref.iter('AcDbAttribute'):
476
                                if find_text_1 != text_node.text and find_text_2 != text_node.text:
477
                                    continue
478
                                elif find_text_1 == text_node.text:
479
                                    x = float(text_node.attrib['X'])
480
                                    y = float(text_node.attrib['Y'])
481
                                    h = float(text_node.attrib['Height'])
482
                                    cad_item_loc_1 = [x, y, h]
483
                                    find_1 = True
484
                                elif find_text_2 == text_node.text:
485
                                    x = float(text_node.attrib['X'])
486
                                    y = float(text_node.attrib['Y'])
487
                                    h = float(text_node.attrib['Height'])
488
                                    cad_item_loc_2 = [x, y, h]
489
                                    find_2 = True
490
                    
491
                if find_1 and find_2:
492
                    dx_1 = abs(text_loc_1[0] - text_loc_2[0])
493
                    dx_2 = abs(cad_item_loc_1[0] - cad_item_loc_2[0])
494
                    scale_x = dx_1 / dx_2
495

    
496
                    dy_1 = abs(text_loc_1[1] - text_loc_2[1])
497
                    dy_2 = abs(cad_item_loc_1[1] - cad_item_loc_2[1])
498
                    scale_y = dy_1 / dy_2
499

    
500
                    offset_x = text_loc_1[0] - cad_item_loc_1[0] * scale_x
501
                    drawing_height = int(id2_xml_root.find('SIZE').text.split(',')[1])
502
                    offset_y = text_loc_1[1] - (drawing_height - cad_item_loc_1[1] * scale_y) + cad_item_loc_1[2] * scale_y
503

    
504
                    self.ui.spinBoxTextX.setValue(offset_x)
505
                    self.ui.spinBoxX.setValue(offset_x)
506
                    self.ui.spinBoxSymbolX.setValue(offset_x)
507
                    self.ui.spinBoxTextY.setValue(offset_y)
508
                    self.ui.spinBoxY.setValue(offset_y)
509
                    self.ui.spinBoxSymbolY.setValue(offset_y)
510
                    self.ui.doubleSpinBox.setValue(scale_x)
511
                    self.ui.doubleSpinBox_2.setValue(scale_y)
512

    
513
            except Exception as ex:
514
                from App import App
515
                from AppDocData import MessageType
516

    
517
                message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
518
                      f'{sys.exc_info()[-1].tb_lineno}'
519
                App.mainWnd().addMessage.emit(MessageType.Error, message)
520

    
521
    def on_load_line_type_mapping(self):
522
        """load ID2-AutoCAD """
523
        def on_remove_treeview_item():
524
            indices = self.ui.treeViewLineType.selectionModel().selectedRows()
525
            for index in sorted(indices):
526
                self.ui.treeViewLineType.model().removeRow(index.row(), index.parent())
527

    
528
        def on_add_line_type_item(index: QModelIndex):
529
            """add AutoCAD line type corresponding to ID2 line type"""
530

    
531
            try:
532
                _index = self.ui.treeViewLineType.model().index(index.row(), 1)
533
                autocad_line_type_item = self.ui.treeViewLineType.model().itemFromIndex(_index)
534
                if autocad_line_type_item and autocad_line_type_item.text():
535
                    _index = self.ui.treeViewLineType.model().index(index.row(), 0, index.parent())
536
                    id2_line_type_item = self.ui.treeViewLineType.model().itemFromIndex(_index)
537
                    items = [QStandardItem(''), QStandardItem(autocad_line_type_item.text()), QStandardItem('')]
538
                    items[0].setData(id2_line_type_item.text(), Qt.UserRole)
539
                    for item in items:
540
                        item.setEditable(False)
541
                    id2_line_type_item.appendRow(items)
542

    
543
                    ## add remove button
544
                    child_count = self.ui.treeViewLineType.model().rowCount(id2_line_type_item.index())
545
                    button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
546
                    button.setMaximumWidth(20)
547
                    button.clicked.connect(on_remove_treeview_item)
548
                    _index = self.ui.treeViewLineType.model().index(child_count - 1, 2, id2_line_type_item.index())
549
                    self.ui.treeViewLineType.setIndexWidget(_index, button)
550

    
551
                    self.ui.treeViewLineType.expandAll()
552
            except Exception as ex:
553
                from App import App
554
                from AppDocData import MessageType
555

    
556
                message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
557
                      f'{sys.exc_info()[-1].tb_lineno}'
558
                App.mainWnd().addMessage.emit(MessageType.Error, message)
559

    
560
        model = LineTypeMappingModel()
561
        model.invisibleRootItem()
562
        self.ui.treeViewLineType.setModel(model)
563
        self.ui.treeViewLineType.setItemDelegate(LineTypeMappingDelegate(self._layer_names, self.ui.treeViewLineType))
564

    
565
        for row in range(model.rowCount()):
566
            button = QPushButton(icon=QIcon(":/newPrefix/Add.svg"))
567
            button.setMaximumWidth(20)
568
            index = self.ui.treeViewLineType.model().index(row, 2)
569
            button.clicked.connect(partial(on_add_line_type_item, index))
570
            self.ui.treeViewLineType.setIndexWidget(index, button)
571

    
572
            parent_index = model.index(row, 0)
573
            child_count = model.rowCount(parent_index)
574
            for child_row in range(child_count):
575
                button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
576
                button.setMaximumWidth(20)
577
                _index = self.ui.treeViewLineType.model().index(child_row, 2, parent_index)
578
                button.clicked.connect(on_remove_treeview_item)
579
                self.ui.treeViewLineType.setIndexWidget(_index, button)
580

    
581
        self.ui.treeViewLineType.resizeColumnToContents(0)
582
        self.ui.treeViewLineType.resizeColumnToContents(1)
583
        self.ui.treeViewLineType.resizeColumnToContents(2)
584
        self.ui.treeViewLineType.expandAll()
585

    
586
        self.ui.label.setHidden(True)
587
        self.ui.spinBoxX.setHidden(True)
588
        self.ui.spinBoxY.setHidden(True)
589
        self.ui.label_8.setHidden(True)
590
        self.ui.spinBoxSymbolX.setHidden(True)
591
        self.ui.spinBoxSymbolY.setHidden(True)
592
        self.ui.label_3.setText('Offset(x, y, text scale) : ')
593

    
594
    def on_remove_symbol_mapping_item(self):
595
        """remove selected items"""
596
        indices = self.ui.treeViewSymbolMapping.selectionModel().selectedRows()
597
        for index in sorted(indices):
598
            _index = self.ui.treeViewSymbolMapping.model().index(index.row(), 1, index.parent())
599
            id2_symbol_item = self.ui.treeViewSymbolMapping.model().itemFromIndex(_index)
600
            self.ui.treeViewSymbolMapping.model()._autocad_symbols.remove(id2_symbol_item.text())
601

    
602
            self.ui.treeViewSymbolMapping.model().removeRow(index.row(), index.parent())
603

    
604
    def on_add_symbol_type_item(self, index: QModelIndex):
605
        """map AutoCAD symbol and ID2 symbol"""
606

    
607
        try:
608
            _index = self.ui.treeViewSymbolMapping.model().index(index.row(), 1, index.parent())
609
            autocad_symbol_item = self.ui.treeViewSymbolMapping.model().itemFromIndex(_index)
610
            if autocad_symbol_item and autocad_symbol_item.text():
611
                _index = self.ui.treeViewSymbolMapping.model().index(index.row(), 0, index.parent())
612
                id2_symbol_item = self.ui.treeViewSymbolMapping.model().itemFromIndex(_index)
613
                items = [QStandardItem(''), QStandardItem(autocad_symbol_item.text()), QStandardItem('')]
614
                items[0].setData(id2_symbol_item.data(Qt.UserRole), Qt.UserRole)
615
                for item in items:
616
                    item.setEditable(False)
617
                id2_symbol_item.appendRow(items)
618

    
619
                self.ui.treeViewSymbolMapping.model()._autocad_symbols.append(id2_symbol_item.text())
620

    
621
                ## add remove button
622
                child_count = self.ui.treeViewSymbolMapping.model().rowCount(id2_symbol_item.index())
623
                button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
624
                button.setMaximumWidth(20)
625
                button.clicked.connect(self.on_remove_symbol_mapping_item)
626
                _index = self.ui.treeViewSymbolMapping.model().index(child_count - 1, 2, id2_symbol_item.index())
627
                self.ui.treeViewSymbolMapping.setIndexWidget(_index, button)
628

    
629
                self.ui.treeViewSymbolMapping.expandAll()
630
        except Exception as ex:
631
            from App import App
632
            from AppDocData import MessageType
633

    
634
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
635
                    f'{sys.exc_info()[-1].tb_lineno}'
636
            App.mainWnd().addMessage.emit(MessageType.Error, message)
637

    
638
    def on_load_symbol_mapping(self):
639
        """
640
        load symbol mapping setting
641
        """
642

    
643
        model = SymbolMappingModel()
644
        model.invisibleRootItem()
645
        self.ui.treeViewSymbolMapping.setModel(model)
646

    
647
        for row in range(model.rowCount()):
648
            parent_index = model.index(row, 0)
649
            child_count = model.rowCount(parent_index)
650
            for child_row in range(child_count):
651
                button = QPushButton(icon=QIcon(":/newPrefix/Add.svg"))
652
                button.setMaximumWidth(20)
653
                index = model.index(child_row, 2, parent_index)
654
                button.clicked.connect(partial(self.on_add_symbol_type_item, index))
655
                self.ui.treeViewSymbolMapping.setIndexWidget(index, button)
656

    
657
                """add autocad symbol item"""
658
                id2_symbol_index = model.index(child_row, 0, parent_index)
659
                acad_symbol_count = model.rowCount(id2_symbol_index)
660
                for acad_symbol in range(acad_symbol_count):
661
                    button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
662
                    button.setMaximumWidth(20)
663
                    _index = model.index(acad_symbol, 2, id2_symbol_index)
664
                    button.clicked.connect(self.on_remove_symbol_mapping_item)
665
                    self.ui.treeViewSymbolMapping.setIndexWidget(_index, button)
666

    
667
        self.ui.treeViewSymbolMapping.resizeColumnToContents(0)
668
        self.ui.treeViewSymbolMapping.resizeColumnToContents(1)
669
        self.ui.treeViewSymbolMapping.resizeColumnToContents(2)
670
        self.ui.treeViewSymbolMapping.expandAll()
671

    
672
        self._symbol_types = self._symbol_types +  model.autocad_symbols[:]
673
        self.ui.treeViewSymbolMapping.setItemDelegate(SymbolMappingDelegate(self._symbol_types,
674
                                                                            self.ui.treeViewSymbolMapping))
675

    
676
    def on_load_exclude_layers_mapping(self) -> None:
677
        """
678
        load exclude layers mapping
679
        :return:
680
        """
681
        model = ExcludeLayersModel(self)
682
        self.ui.listViewExcludeLayers.setModel(model)
683

    
684
    def on_add_cad_click(self):
685
        """
686
        extract information from select autocad files
687
        :return:
688
        """
689
        project = AppDocData.instance().getCurrentProject()
690
        drawing_path = project.getDrawingFilePath()
691

    
692
        options = QFileDialog.Options()
693
        options |= QFileDialog.DontUseNativeDialog
694
        files, _ = QFileDialog.getOpenFileNames(self, self.tr('Import', "Select AutoCAD File"),
695
                                                os.path.join(drawing_path, 'Native'),
696
                                                "dwg files (*.dwg)", options=options)
697
        if files:
698
            self.ui.lineEditCAD.setText(files[0]) if len(files) == 1 else \
699
                self.ui.lineEditCAD.setText(str(len(files)) + ' files')
700

    
701
            self._dwgs.clear()
702
            self._line_types.clear()
703
            self._symbol_types.clear()
704
            self._layer_names.clear()
705
            for _file in files:
706
                if os.path.exists(_file):
707
                    try:
708
                        file = os.path.normpath(_file)
709
                        drawing_name = os.path.splitext(os.path.basename(file))[0]
710
                        executable = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'OdReadExMgd',
711
                                                  'OdReadExMgd.exe')
712
                        args = [executable, file, '0', '0', '1', '1' if int(self.ui.checkBoxGenDrawing.checkState()) is int(Qt.Checked) else '0']
713
                        CREATE_NO_WINDOW = 0x08000000
714
                        p = subprocess.Popen(
715
                            args,
716
                            stdin=subprocess.PIPE,
717
                            stdout=subprocess.PIPE,
718
                            stderr=subprocess.PIPE,
719
                            creationflags=CREATE_NO_WINDOW
720
                        )
721

    
722
                        stdout, stderr = p.communicate()
723
                        if len(stderr) != 0 or ('eFileSharingViolation' in str(stdout) or 300 > len(stdout)):
724
                            #raise RuntimeError('OdReadExMgd threw error:\n' + stderr.decode('utf-8'))
725
                            raise RuntimeError('OdReadExMgd threw error:\n' + str(stderr)) if len(stderr) != 0 else \
726
                                RuntimeError('OdReadExMgd threw error:\n' + str(stdout))
727

    
728
                        """load image for graphic"""
729
                        drawing_file_path = os.path.join(drawing_path, drawing_name + '.PNG')
730
                        if not os.path.exists(drawing_file_path):
731
                            continue
732
                        drawing = Drawing(str(uuid.uuid4()), drawing_name + ".PNG", None)
733
                        _image = drawing.image
734
                        bytesPerLine = _image.shape[1]
735
                        image = QImage(_image.data, _image.shape[1], _image.shape[0], bytesPerLine, QImage.Format_Indexed8)
736
                        id2_bbox = self.get_contents_size_from_image(drawing_file_path)
737
                        """up to here"""
738

    
739
                        """parse layer, line type and symbol"""
740
                        autocad_xml_path = os.path.join(os.path.dirname(file), drawing_name + '.xml')
741
                        autocad_xml = parse(autocad_xml_path)
742
                        autocad_xml_root = autocad_xml.getroot()
743

    
744
                        for layer_tbl_record in autocad_xml_root.iter('AcDbLayerTableRecord'):
745
                            layer_name = layer_tbl_record.attrib['Name']
746
                            if layer_name in self._layer_names:
747
                                continue
748

    
749
                            self._layer_names.append(layer_name)
750

    
751
                        for line_type_tbl_record in autocad_xml_root.iter('AcDbLinetypeTableRecord'):
752
                            line_type_name = line_type_tbl_record.attrib['Name']
753
                            if line_type_name.upper() in ['ByBlock'.upper(), 'ByLayer'.upper()] or line_type_name in self._line_types:
754
                                continue
755

    
756
                            self._line_types.append(line_type_tbl_record.attrib['Name'])
757

    
758
                        for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
759
                            if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
760
                                continue
761

    
762
                            min_values = [float(token) for token in
763
                                        blk_tbl_record.attrib['MinExtents'].strip('()').split(',')]
764
                            max_values = [float(token) for token in
765
                                        blk_tbl_record.attrib['MaxExtents'].strip('()').split(',')]
766
                            autocad_bbox = [min_values[0], min_values[1],
767
                                            max_values[0] - min_values[0], max_values[1] - min_values[1]]
768

    
769
                            if self.ui.checkBoxAuto.isChecked():
770
                                scale_x = id2_bbox[2] / autocad_bbox[2]
771
                                scale_y = id2_bbox[3] / autocad_bbox[3]
772
                                self.scales = [scale_x, scale_y]
773
                                offsets = [id2_bbox[0] - autocad_bbox[0] * scale_x, id2_bbox[3] + id2_bbox[1]]
774
                                self.offsets = offsets + [autocad_bbox[1]]
775
                            else:
776
                                self.scales = [self.ui.doubleSpinBox.value(), self.ui.doubleSpinBox_2.value()]
777
                                self.offsets = [self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), id2_bbox[5]]
778

    
779
                        for block_ref_record in autocad_xml_root.iter('AcDbBlockReference'):
780
                            block_name = block_ref_record.attrib['Name']
781
                            if block_name in self._symbol_types:
782
                                continue
783
                            
784
                            self._symbol_types.append(block_name)
785

    
786
                            '''save graphic'''
787
                            #node = self.symbol_info(block_ref_record)
788
                            #if node and block_name.split('+')[0] == 'Graphic':
789
                            #    symbolImage = image.copy(round(node[2][0]), round(node[2][1]), math.ceil(node[3] + 2), math.ceil(node[4] + 1))
790
                            #    file_name = drawing_name + '++' + block_name + '++' + str(round(node[2][0])) + '++' + str(round(node[2][1]))
791
                            #    image_file_path = os.path.join(project.getGraphicFilePath(), file_name + '.PNG')
792
                            #    symbolImage.save(image_file_path, 'PNG')
793
                            '''up to here'''
794
                        """up to here"""
795

    
796
                        self._dwgs.append(file)
797
                    except Exception as ex:
798
                        from App import App
799
                        from AppDocData import MessageType
800

    
801
                        message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
802
                                  f"{sys.exc_info()[-1].tb_lineno}"
803
                        App.mainWnd().addMessage.emit(MessageType.Error, message)
804

    
805
            self.ui.listViewExcludeLayers.model().append_layers(self._layer_names)
806

    
807
            return self._symbol_types
808

    
809
    def on_save_mappings(self):
810
        """save line type and symbol mappings"""
811
        import io
812
        import csv
813
        from AppDocData import Config
814

    
815
        configs = []
816
        configs.append(Config('Cad Offset', 'X', self.ui.spinBoxX.value()))
817
        configs.append(Config('Cad Offset', 'Y', self.ui.spinBoxY.value()))
818
        configs.append(Config('Cad Offset', 'Text X', self.ui.spinBoxTextX.value()))
819
        configs.append(Config('Cad Offset', 'Text Y', self.ui.spinBoxTextY.value()))
820
        configs.append(Config('Cad Offset', 'Scale', self.ui.doubleSpinBoxScale.value()))
821
        configs.append(Config('Cad Offset', 'X Scale', self.ui.doubleSpinBox.value()))
822
        configs.append(Config('Cad Offset', 'Y Scale', self.ui.doubleSpinBox_2.value()))
823
        configs.append(Config('Cad State', 'Auto', int(self.ui.checkBoxAuto.isChecked())))
824
        configs.append(Config('Cad State', 'Line', int(self.ui.checkBoxLine.isChecked())))
825
        configs.append(Config('Cad State', 'Text', int(self.ui.checkBoxText.isChecked())))
826
        configs.append(Config('Cad State', 'Symbol', int(self.ui.checkBoxSymbol.isChecked())))
827

    
828
        model = self.ui.treeViewLineType.model()
829
        for row in range(model.rowCount()):
830
            parent_index = model.index(row, 0)
831
            id2_line_type = model.itemFromIndex(parent_index).text()
832

    
833
            autocad_line_types = []
834
            child_count = model.rowCount(parent_index)
835
            for child_row in range(child_count):
836
                child_index = model.index(child_row, 1, parent_index)
837
                autocad_line_types.append(model.itemFromIndex(child_index).text())
838

    
839
            if autocad_line_types:
840
                configs.append(Config('Line Type Mapping', id2_line_type, ','.join(autocad_line_types)))
841

    
842
        # save symbol mappings
843
        model = self.ui.treeViewSymbolMapping.model()
844
        for row in range(model.rowCount()):
845
            category_index = model.index(row, 0)
846

    
847
            child_count = model.rowCount(category_index)
848
            for child_row in range(child_count):
849
                autocad_symbols = []
850
                child_index = model.index(child_row, 0, category_index)
851
                id2_symbol_item = model.itemFromIndex(child_index)
852
                id2_symbol_uid = id2_symbol_item.data(Qt.UserRole)
853
                id2_symbol = id2_symbol_item.text()
854
                mapping_count = model.rowCount(child_index)
855
                for mapping_row in range(mapping_count):
856
                    mapping_index = model.index(mapping_row, 1, child_index)
857
                    autocad_symbols.append(model.itemFromIndex(mapping_index).text())
858

    
859
                if autocad_symbols:
860
                    configs.append(Config('Symbol Mapping', id2_symbol_uid, ','.join(autocad_symbols)))
861

    
862
        # save exclude layers mappings
863
        model = self.ui.listViewExcludeLayers.model()
864
        exclude_layers = []
865
        for row in range(model.rowCount()):
866
            item = model.itemFromIndex(model.index(row, 0))
867
            if item.checkState() == Qt.Checked:
868
                exclude_layers.append(item.text())
869

    
870
        if exclude_layers:
871
            stream = io.StringIO()
872
            csv.writer(stream, delimiter=',', escapechar='^', quoting=csv.QUOTE_NONE).writerow(exclude_layers)
873
            configuration = stream.getvalue()
874

    
875
            configs.append(Config('Exclude Layers Mapping', 'All', configuration))
876

    
877
        app_doc_data = AppDocData.instance()
878
        """delete line type mapping and symbol mapping"""
879
        app_doc_data.deleteConfigs(section='Line Type Mapping')
880
        app_doc_data.deleteConfigs(section='Symbol Mapping')
881
        app_doc_data.deleteConfigs(section='Exclude Layers Mapping')
882

    
883
        app_doc_data.saveConfigs(configs)
884

    
885
        QMessageBox.information(self, self.tr('Information'), self.tr('Successfully saved.'))
886
        """up to here"""
887

    
888
    def on_import_legend(self):
889
        import XmlGenerator as xg
890
        from App import App
891

    
892
        if not self._dwgs:
893
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file'))
894
            return
895

    
896
        app_doc_data = AppDocData.instance()
897
        project = app_doc_data.getCurrentProject()
898

    
899
        temp_path, drawing_path = project.getTempPath(), project.getDrawingFilePath()
900
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
901
                         (os.path.splitext(f)[1].upper() == '.XML')]
902

    
903
        for _file in self._dwgs:
904
            file_name = os.path.splitext(os.path.basename(_file))[0]
905
            id2_image_file = os.path.join(drawing_path, file_name + '.png')
906
            autocad_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
907
            matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
908

    
909
            if not matches:
910
                path = os.path.join(app_doc_data.getCurrentProject().getTempPath(), file_name + '.xml')
911
                img = AppDocData.my_imread(file_path=id2_image_file)
912
                _width = img.shape[1]
913
                _height = img.shape[0]
914
                xml, result = xg.write_to_xml(path, _width, _height, [], [])
915
                if xml:
916
                    from xml.etree import ElementTree
917
                    ElementTree.ElementTree(xml).write(path)
918
                    matches = [file_name + '.xml']
919
        
920
            if matches and os.path.exists(id2_image_file):
921
                try:
922
                    symbols = {}
923

    
924
                    drawing = Drawing(str(uuid.uuid4()), file_name + ".PNG", None)
925
                    _image = drawing.image#QImage(id2_image_file, "PNG")
926
                    bytesPerLine = _image.shape[1]
927
                    image = QImage(_image.data, _image.shape[1], _image.shape[0], bytesPerLine, QImage.Format_Indexed8)
928
                    autocad_xml = parse(autocad_xml_path)
929

    
930
                    autocad_xml_root = autocad_xml.getroot()
931

    
932
                    id2_bbox = self.get_contents_size_from_image(id2_image_file)
933

    
934
                    for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
935
                        if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
936
                            continue
937

    
938
                        min_values = [float(token) for token in
939
                                      blk_tbl_record.attrib['MinExtents'].strip('()').split(',')]
940
                        max_values = [float(token) for token in
941
                                      blk_tbl_record.attrib['MaxExtents'].strip('()').split(',')]
942
                        autocad_bbox = [min_values[0], min_values[1],
943
                                        max_values[0] - min_values[0], max_values[1] - min_values[1]]
944

    
945
                        if self.ui.checkBoxAuto.isChecked():
946
                            scale_x = id2_bbox[2] / autocad_bbox[2]
947
                            scale_y = id2_bbox[3] / autocad_bbox[3]
948
                            self.scales = [scale_x, scale_y]
949
                            offsets = [id2_bbox[0] - autocad_bbox[0] * scale_x, id2_bbox[3] + id2_bbox[1]]
950
                            self.offsets = offsets + [autocad_bbox[1]]
951
                        else:
952
                            self.scales = [self.ui.doubleSpinBox.value(), self.ui.doubleSpinBox_2.value()]
953
                            self.offsets = [self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), id2_bbox[5]]
954

    
955
                        for blk_ref in blk_tbl_record.iter('AcDbBlockReference'):
956
                            node = self.symbol_info(blk_ref)
957
                            if node[0] not in symbols:
958
                                symbols[node[0]] = node
959

    
960
                    dialog = QSymbolRegiDataListDialog(self, image, symbols)
961
                    create = dialog.showDialog()
962
                    if create:
963
                        App.mainWnd().symbolTreeWidget.initSymbolTreeView()
964
                    
965
                    '''
966
                    for key, value in symbols.items():
967
                        symbolEditorDialog = QSymbolEditorDialog(self, image.copy(math.floor(value[2][0]), math.floor(value[2][1]), \
968
                                                                    math.ceil(value[3] + 1), math.ceil(value[4] + 1)), \
969
                                                                    AppDocData.instance().getCurrentProject(), value[1], False)
970
                        (isAccepted, _, _, _, _) = symbolEditorDialog.showDialog()
971

972
                        if not isAccepted:
973
                            reply = QMessageBox.question(self, self.tr('Stop?'), self.tr('Do you want to stop registering symbols?'), QMessageBox.Yes, QMessageBox.Cancel)
974
                            if reply == QMessageBox.Yes:
975
                                break
976
                    '''
977

    
978
                except Exception as ex:
979
                    from App import App 
980
                    from AppDocData import MessageType
981

    
982
                    message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
983
                              f"{sys.exc_info()[-1].tb_lineno}"
984
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
985
            else:
986
                from App import App
987
                from AppDocData import MessageType
988

    
989
                message = 'There is no xml file'
990
                App.mainWnd().addMessage.emit(MessageType.Error, message)
991

    
992
    def get_contents_size_from_image(self, img_file_path: str) -> list:
993
        """get actual size of image from image file"""
994
        from AppDocData import AppDocData
995
        import cv2
996

    
997
        min_x, min_y, max_x, max_y, width, height = None, None, None, None, None, None
998

    
999
        img = AppDocData.my_imread(file_path=img_file_path)
1000
        _width = img.shape[1]
1001
        _height = img.shape[0]
1002
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1003
        img = cv2.bitwise_not(img)  # background should be 'BLACK'
1004
        contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
1005
        for cnt in contours:
1006
            x, y, width, height = cv2.boundingRect(cnt)
1007
            min_x = x if not min_x else min(x, min_x)
1008
            min_y = y if not min_y else min(y, min_y)
1009
            max_x = x + width if not max_x else max(x + width, max_x)
1010
            max_y = y + height if not max_y else max(y + height, max_y)
1011

    
1012
        del img
1013

    
1014
        return min_x, min_y, max_x - min_x, max_y - min_y, _width, _height
1015

    
1016
    def on_import_autocad(self):
1017
        """
1018
        import line, text and symbol from autocad
1019
        @return: None
1020
        """
1021

    
1022
        import XmlGenerator as xg
1023

    
1024
        if not self._dwgs:
1025
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file(s)'))
1026
            return
1027

    
1028
        app_doc_data = AppDocData.instance()
1029
        project = app_doc_data.getCurrentProject()
1030

    
1031
        temp_path, drawing_path = project.getTempPath(), project.getDrawingFilePath()
1032
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
1033
                         (os.path.splitext(f)[1].upper() == '.XML')]
1034

    
1035
        self.text_scale = self.ui.doubleSpinBoxScale.value()
1036

    
1037
        """get exclude layers mapping"""
1038
        exclude_layers = []
1039
        model = self.ui.listViewExcludeLayers.model()
1040
        for row in range(model.rowCount()):
1041
            item = model.itemFromIndex(model.index(row, 0))
1042
            if item.checkState() == Qt.Checked:
1043
                exclude_layers.append(item.text())
1044

    
1045
        """get items to be converted"""
1046
        will_be_converted_items = []
1047
        if self.ui.checkBoxLine.checkState() == Qt.Checked:
1048
            will_be_converted_items.append('Line')
1049
        if self.ui.checkBoxText.checkState() == Qt.Checked:
1050
            will_be_converted_items.append('Text')
1051
        if self.ui.checkBoxSymbol.checkState() == Qt.Checked:
1052
            will_be_converted_items.append('Symbol')
1053

    
1054
        mapping_configs = app_doc_data.getConfigs(section='Symbol Mapping')
1055

    
1056
        for _file in self._dwgs:
1057
            file_name = os.path.splitext(os.path.basename(_file))[0]
1058
            id2_image_file = os.path.join(drawing_path, file_name + '.png')
1059
            autocad_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
1060
            matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
1061

    
1062
            if not matches:
1063
                path = os.path.join(app_doc_data.getCurrentProject().getTempPath(), file_name + '.xml')
1064
                img = AppDocData.my_imread(file_path=id2_image_file)
1065
                _width = img.shape[1]
1066
                _height = img.shape[0]
1067
                xml, result = xg.write_to_xml(path, _width, _height, [], [])
1068
                if xml:
1069
                    from xml.etree import ElementTree
1070
                    ElementTree.ElementTree(xml).write(path)
1071
                    matches = [file_name + '.xml']
1072

    
1073
            if matches and os.path.exists(id2_image_file):
1074
                try:
1075
                    layers, line_types, symbols = [], [], []
1076
                    autocad_xml = parse(autocad_xml_path)
1077

    
1078
                    """parse layer, line type and symbol"""
1079
                    autocad_xml_root = autocad_xml.getroot()
1080
                    for layer_tbl_record in autocad_xml_root.iter('AcDbLayerTableRecord'):
1081
                        layer_name = layer_tbl_record.attrib['Name']
1082
                        line_type_oid = layer_tbl_record.attrib['LinetypeObjectId']
1083
                        layers.append([layer_name, line_type_oid])
1084

    
1085
                    for line_type_tbl_record in autocad_xml_root.iter('AcDbLinetypeTableRecord'):
1086
                        line_type_oid = line_type_tbl_record.attrib['ObjectId']
1087
                        line_type_name = line_type_tbl_record.attrib['Name']
1088
                        line_types.append([line_type_oid, line_type_name])
1089

    
1090
                    '''
1091
                    for blk_ref_record in autocad_xml_root.iter('AcDbBlockReference'):
1092
                        blk_ref_handle = blk_ref_record.attrib['Handle']
1093
                        blk_ref_name = blk_ref_record.attrib['Name']
1094
                        symbols.append([blk_ref_handle, blk_ref_name])
1095
                    '''
1096
                    """up to here"""
1097

    
1098
                    id2_xml_path = os.path.join(temp_path, matches[0])
1099
                    id2_xml = parse(id2_xml_path)
1100
                    id2_xml_root = id2_xml.getroot()
1101

    
1102
                    #id2_bbox = [0, 0, 9600, 6781]
1103
                    id2_bbox = self.get_contents_size_from_image(id2_image_file)
1104

    
1105
                    if 'Symbol' in will_be_converted_items:
1106
                        symbols_node = id2_xml_root.find(xg.SYMBOL_LIST_NODE_NAME)
1107
                        if symbols_node:
1108
                            symbols = symbols_node.findall('SYMBOL')
1109
                            # remove only converted symbol nodes
1110
                            for symbol in symbols:
1111
                                if 'Converted' in symbol.attrib and symbol.attrib['Converted'] == str(True):
1112
                                    symbols_node.remove(symbol)
1113
                        graphics_node = id2_xml_root.find(xg.GRAPHIC_NODE_NAME)
1114
                        graphics_node.clear()
1115

    
1116
                    if 'Text' in will_be_converted_items:
1117
                        """remove texts from id2 xml file"""
1118
                        textInfo = id2_xml_root.find(xg.TEXT_INFO_LIST_NODE_NAME)
1119
                        textInfo.clear()
1120

    
1121
                        noteInfo = id2_xml_root.find(xg.NOTE_TEXT_INFO_LIST_NOTE_NAME)
1122
                        noteInfo.clear()
1123

    
1124
                        lineNoInfo = id2_xml_root.find(xg.LINE_NOS_NODE_NAME)
1125
                        lineNoInfo.clear()
1126
                        """up to here"""
1127

    
1128
                    if 'Line' in will_be_converted_items:
1129
                        """remove lines from id2 xml file"""
1130
                        line_infos = id2_xml_root.find(xg.LINE_INFOS_NODE_NAME)
1131
                        line_infos.clear()
1132
                        """up to here"""
1133

    
1134
                    """remove unknowns from id2 xml file"""
1135
                    unknowns = id2_xml_root.find(xg.UNKNOWNS_NODE_NAME)
1136
                    unknowns.clear()
1137
                    """up to here"""
1138

    
1139
                    """add text, line and symbol from autocad file to id2 xml file"""
1140
                    for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
1141
                        if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
1142
                            continue
1143

    
1144
                        min_values = [float(token) for token in
1145
                                      blk_tbl_record.attrib['MinExtents'].strip('()').split(',')]
1146
                        max_values = [float(token) for token in
1147
                                      blk_tbl_record.attrib['MaxExtents'].strip('()').split(',')]
1148
                        autocad_bbox = [min_values[0], min_values[1],
1149
                                        max_values[0] - min_values[0], max_values[1] - min_values[1]]
1150

    
1151
                        if self.ui.checkBoxAuto.isChecked():
1152
                            scale_x = id2_bbox[2] / autocad_bbox[2]
1153
                            scale_y = id2_bbox[3] / autocad_bbox[3]
1154
                            self.scales = [scale_x, scale_y]
1155
                            offsets = [id2_bbox[0] - autocad_bbox[0] * scale_x, id2_bbox[3] + id2_bbox[1]]
1156
                            self.offsets = offsets + [autocad_bbox[1]]
1157
                        else:
1158
                            self.scales = [self.ui.doubleSpinBox.value(), self.ui.doubleSpinBox_2.value()]
1159
                            self.offsets = [self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), id2_bbox[5]]
1160

    
1161
                        if 'Text' in will_be_converted_items:
1162
                            for record in blk_tbl_record.iter('AcDbText'):
1163
                                if record.attrib['Layer'] not in exclude_layers:
1164
                                    node = self.text_to_xml(record)
1165
                                    if node:
1166
                                        textInfo.append(node)
1167

    
1168
                        for blk_ref in blk_tbl_record.iter('AcDbBlockReference'):
1169
                            if 'Symbol' in will_be_converted_items:
1170
                                if blk_ref.attrib['Layer'] not in exclude_layers:
1171
                                    node = None
1172
                                    block_name = blk_ref.attrib['Name']
1173
                                    if block_name.split('+')[0] == 'Graphic':
1174
                                        node = self.graphic_to_xml(blk_ref)
1175
                                        if node:
1176
                                            graphics_node.append(node)
1177
                                    else:
1178
                                        node = self.symbol_to_xml(blk_ref, mapping_configs)
1179
                                        if node:
1180
                                            symbols_node.append(node)
1181

    
1182
                            if 'Text' in will_be_converted_items:
1183
                                #angle = round(float(blk_ref.attrib['Angle']), 2)
1184
                                for record in blk_ref.iter('AcDbAttribute'):
1185
                                    node = self.text_to_xml(record)
1186
                                    if node:
1187
                                        textInfo.append(node)
1188

    
1189
                        if 'Line' in will_be_converted_items:
1190
                            for record in blk_tbl_record.iter('AcDbLine'):
1191
                                if record.attrib['Layer'] not in exclude_layers:
1192
                                    nodes = self.lines_to_xml(layers, line_types, record)
1193
                                    if nodes:
1194
                                        for node in nodes:
1195
                                            line_infos.append(node)
1196

    
1197
                            for record in blk_tbl_record.iter('AcDbArc'):
1198
                                if record.attrib['Layer'] not in exclude_layers:
1199
                                    nodes = self.lines_to_xml(layers, line_types, record)
1200
                                    if nodes:
1201
                                        for node in nodes:
1202
                                            line_infos.append(node)
1203

    
1204
                            for record in blk_tbl_record.iter('AcDbPolyline'):
1205
                                if record.attrib['Layer'] not in exclude_layers:
1206
                                    nodes = self.lines_to_xml(layers, line_types, record)
1207
                                    if nodes:
1208
                                        for node in nodes:
1209
                                            line_infos.append(node)
1210

    
1211
                    id2_xml.write(id2_xml_path, encoding="utf-8", xml_declaration=True)
1212
                    """up to here"""
1213

    
1214
                except Exception as ex:
1215
                    from App import App 
1216
                    from AppDocData import MessageType
1217

    
1218
                    message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1219
                              f"{sys.exc_info()[-1].tb_lineno}"
1220
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1221
            else:
1222
                from App import App
1223
                from AppDocData import MessageType
1224

    
1225
                message = 'There is no xml file'
1226
                App.mainWnd().addMessage.emit(MessageType.Error, message)
1227

    
1228
        QMessageBox.information(self, self.tr('Information'), self.tr('Importing finished!'),
1229
                                QMessageBox.Close)
1230

    
1231
    def symbol_info(self, blk_ref_node):
1232
        """try to convert block reference element to id2 xml"""
1233
        import symbol
1234

    
1235
        try:
1236
            symbolTypes = [symbolType[2] for symbolType in AppDocData.instance().getSymbolTypeList()]
1237
            _name = blk_ref_node.attrib['Name']
1238
            if '+' in _name and _name.split('+')[0] in symbolTypes:
1239
                name = _name.split('+')[1]
1240
                type = _name.split('+')[0]
1241
            elif '+' in _name:
1242
                name = _name.split('+')[1]
1243
                type = 'Valves' if _name.split('+')[0] != 'Equipment' else 'Vessels'
1244
            else:
1245
                name = _name
1246
                type = 'Valves'
1247
            _origin = self.convert_to_image_coords([float(blk_ref_node.attrib['X']), float(blk_ref_node.attrib['Y'])])
1248

    
1249
            """check if maxtents or minextents attribute exists"""
1250
            if 'MaxExtents' not in blk_ref_node.attrib or 'MinExtents' not in blk_ref_node.attrib:
1251
                return None
1252

    
1253
            min_extents, max_extents = None, None
1254
            tokens = blk_ref_node.attrib['MaxExtents'].strip('()').split(',')
1255
            if 3 == len(tokens):
1256
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1257

    
1258
            tokens = blk_ref_node.attrib['MinExtents'].strip('()').split(',')
1259
            if 3 == len(tokens):
1260
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1261

    
1262
            _height = math.ceil(abs(max_extents[1] - min_extents[1]))
1263
            _width = math.ceil(abs(max_extents[0] - min_extents[0]))
1264

    
1265
            loc = [min(min_extents[0], max_extents[0]), min(min_extents[1], max_extents[1])]
1266

    
1267
            origin = [math.ceil(_origin[0] - loc[0]), math.ceil(_origin[1] - loc[1])]
1268
            origin[0] = 0 if origin[0] <= 0 else (_width if origin[0] > _width else origin[0])
1269
            origin[1] = 0 if origin[1] <= 0 else (_height if origin[1] > _height else origin[1])
1270

    
1271
            points = []
1272
            if blk_ref_node.attrib['Nodes']:
1273
                _points = blk_ref_node.attrib['Nodes'].replace('(', '').replace(')', '').split('/')
1274
                if _points:
1275
                    for _point in _points:
1276
                        _point = _point.split(',')
1277
                        point = self.convert_to_image_coords([float(_point[0]), float(_point[1])])
1278
                        point[0] = 0 if math.ceil(point[0] - loc[0]) <= 0 else (_width if math.ceil(point[0] - loc[0]) > _width else math.ceil(point[0] - loc[0]))
1279
                        point[1] = 0 if math.ceil(point[1] - loc[1]) <= 0 else (_height if math.ceil(point[1] - loc[1]) > _height else math.ceil(point[1] - loc[1]))
1280
                        points.append(point)
1281

    
1282
            strPoints = []
1283
            for point in points:
1284
                strPoint = 'AUTO,' + str(point[0]) + ',' + str(point[1]) + ',0,None,X,Secondary'
1285
                strPoints.append(strPoint)
1286

    
1287
            ret = symbol.SymbolBase(name, type, 0.75, 0, 1, 0, 0, 0, \
1288
                                    "{},{}".format(origin[0], origin[1]), \
1289
                                    '/'.join(strPoints), \
1290
                                    'None', '', 0, 0, -1, \
1291
                                    iType=-2, detectFlip=0, text_area='')
1292

    
1293
            #symbolEditorDialog = QSymbolEditorDialog(self, image.copy(math.floor(loc[0]), math.floor(int(loc[1])), math.ceil(_width + 1), math.ceil(_height + 1)), \
1294
            #                                        AppDocData.instance().getCurrentProject(), ret, False)
1295
            #(isAccepted, isImmediateInsert, offsetX, offsetY, newSym) = symbolEditorDialog.showDialog()
1296

    
1297
            return [_name, ret, loc, _width, _height]
1298
        except Exception as ex:
1299
            from App import App
1300
            from AppDocData import MessageType
1301

    
1302
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1303
                      f"{sys.exc_info()[-1].tb_lineno}"
1304
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1305

    
1306
        return None
1307

    
1308
    def convert_to_image_coords(self, pt):
1309
        """convert autocad coordinates to image coordinates"""
1310
        if self.ui.checkBoxAuto.isChecked():
1311
            return [pt[0] * self.scales[0] + self.offsets[0], (self.offsets[2] - pt[1]) * self.scales[1] + self.offsets[1]]
1312
        else:
1313
            return [pt[0] * self.scales[0] + self.offsets[0], self.offsets[2] - pt[1] * self.scales[1] + self.offsets[1]]
1314

    
1315
    def graphic_to_xml(self, blk_ref_node):
1316
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1317

    
1318
        try:
1319
            node = Element('GRAPHIC')
1320
            uidNode = Element('UID')
1321
            uidNode.text = str(uuid.uuid4())
1322
            node.append(uidNode)
1323

    
1324
            nameNode = Element('NAME')
1325
            nameNode.text = blk_ref_node.attrib['Name']
1326
            node.append(nameNode)
1327

    
1328
            min_extents, max_extents = None, None
1329
            tokens = blk_ref_node.attrib['MaxExtents'].strip('()').split(',')
1330
            if 3 == len(tokens):
1331
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1332

    
1333
            tokens = blk_ref_node.attrib['MinExtents'].strip('()').split(',')
1334
            if 3 == len(tokens):
1335
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1336

    
1337
            locNode1 = Element('LOCATION1')
1338
            locNode1.text = '{},{}'.format(min_extents[0], min_extents[1])
1339
            node.append(locNode1)
1340

    
1341
            locNode2 = Element('LOCATION2')
1342
            locNode2.text = '{},{}'.format(max_extents[0], max_extents[1])
1343
            node.append(locNode2)
1344

    
1345
        except Exception as ex:
1346
            from App import App
1347
            from AppDocData import MessageType
1348

    
1349
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1350
                                                           sys.exc_info()[-1].tb_lineno)
1351
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1352

    
1353
            return None
1354

    
1355
        return node
1356

    
1357
    def symbol_to_xml(self, blk_ref_node, mapping_configs: list) -> str:
1358
        """try to convert block reference element to id2 xml"""
1359
        from SymbolSvgItem import SymbolSvgItem
1360

    
1361
        try:
1362
            name = blk_ref_node.attrib['Name']
1363
            origin = self.convert_to_image_coords([float(blk_ref_node.attrib['X']), float(blk_ref_node.attrib['Y'])])
1364
            flip = float(blk_ref_node.attrib['ScaleFactors'].replace('(', '').replace(')', '').split(',')[0])
1365
            flip = 1 if flip < 0 else 0
1366
            if flip == 0:
1367
                angle = round(2 * math.pi - float(blk_ref_node.attrib['Angle']), 2)
1368
            else:
1369
                angle = round(float(blk_ref_node.attrib['Angle']), 2)
1370

    
1371
            if angle > 6.28:
1372
                angle = angle - 2 * math.pi
1373
            elif angle == 6.28:
1374
                angle = 0.0
1375

    
1376
            """check if maxtents or minextents attribute exists"""
1377
            if 'MaxExtents' not in blk_ref_node.attrib or 'MinExtents' not in blk_ref_node.attrib:
1378
                return None
1379

    
1380
            min_extents, max_extents = None, None
1381
            tokens = blk_ref_node.attrib['MaxExtents'].strip('()').split(',')
1382
            if 3 == len(tokens):
1383
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1384

    
1385
            tokens = blk_ref_node.attrib['MinExtents'].strip('()').split(',')
1386
            if 3 == len(tokens):
1387
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1388

    
1389
            _height = abs(max_extents[1] - min_extents[1])
1390
            _width = abs(max_extents[0] - min_extents[0])
1391

    
1392
            '''
1393
            allowed_error = 0.01
1394
            if abs(angle - 1.57) < allowed_error:
1395
                _height, _width = _width, _height
1396
                #origin[0], origin[1] = origin[0] - _width, origin[1] - _height + _width
1397
            elif abs(angle - 4.71) < allowed_error:
1398
                _height, _width = _width, _height
1399
                #origin[0], origin[1] = origin[0] - _width, origin[1] + _height - _width
1400
            '''
1401

    
1402
            uid = None
1403
            for config in mapping_configs:
1404
                if name in config.value.split(','):
1405
                    uid = config.key
1406
                    break
1407
            if uid:
1408
                app_doc_data = AppDocData.instance()
1409
                symbol = app_doc_data.getSymbolByQuery('UID', uid)
1410
                svg_file_path = os.path.join(app_doc_data.getCurrentProject().getSvgFilePath(), symbol.getType(),
1411
                                             symbol.getName() + '.svg')
1412

    
1413
                item = SymbolSvgItem.createItem(symbol.getType(), symbol.getName(), path=svg_file_path)
1414
                loc = [min(min_extents[0], max_extents[0]), min(min_extents[1], max_extents[1])]
1415
                item.buildItem(name, symbol.getType(), angle, loc, (_width, _height), origin,
1416
                               connPts=symbol.parse_connection_pts(origin), parentSymbol=None, childSymbol=None,
1417
                               hasInstrumentLabel=False, dbUid=uid)
1418
                item.converted = True
1419
                item.flip = flip
1420

    
1421
                app_doc_data = AppDocData.instance()
1422
                for area in app_doc_data.getAreaList():
1423
                    if area.contains([origin[0], origin[1]]):
1424
                        item.area = area.name
1425
                        break
1426

    
1427
                return item.toXml()
1428
        except Exception as ex:
1429
            from App import App
1430
            from AppDocData import MessageType
1431

    
1432
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1433
                      f"{sys.exc_info()[-1].tb_lineno}"
1434
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1435

    
1436
        return None
1437

    
1438
    def text_to_xml(self, text_node):
1439
        """
1440
        try to convert text element to id2 xml
1441
        @param text_node:
1442
        @param id2_bbox:
1443
        @param autocad_bbox:
1444
        @return:
1445
        """
1446
        import math
1447
        from EngineeringTextItem import QEngineeringTextItem
1448

    
1449
        try:
1450
            loc = self.convert_to_image_coords([float(text_node.attrib['X']), float(text_node.attrib['Y'])])
1451

    
1452
            text = text_node.text
1453
            angle = round(float(text_node.attrib['Angle']), 2)
1454
            if 'IsMirroredInX' in text_node.attrib and text_node.attrib['IsMirroredInX'] == 'True':
1455
                if abs(math.pi * 0.5 - angle) < 0.01 or abs(math.pi * 1.5 - angle) < 0.01:
1456
                    angle += math.pi
1457
                    angle = angle - math.pi * 2 if angle > math.pi * 2 else angle
1458

    
1459
            if 'IsMirroredInY' in text_node.attrib and text_node.attrib['IsMirroredInY'] == 'True':
1460
                if abs(angle) < 0.01 or abs(math.pi - angle) < 0.01:
1461
                    angle += math.pi
1462
                    angle = angle - math.pi * 2 if angle > math.pi * 2 else angle
1463

    
1464
            _height = round(float(text_node.attrib['Height']) * self.scales[1])
1465
            loc[1] -= _height
1466
            _width = round(_height * len(text) * self.text_scale)
1467
            #_width = round(float(text_node.attrib['Width']))
1468

    
1469
            allowed_error = 0.01
1470
            if abs(angle - 1.57) < allowed_error:
1471
                _height, _width = _width, _height
1472
                loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
1473
            elif abs(angle - 4.71) < allowed_error:
1474
                _height, _width = _width, _height
1475
                loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
1476

    
1477
            item = QEngineeringTextItem()
1478
            item.setPlainText(text)
1479
            item.loc = loc
1480
            item.size = (_width, _height)
1481
            item.angle = angle
1482

    
1483
            app_doc_data = AppDocData.instance()
1484
            for area in app_doc_data.getAreaList():
1485
                if area.contains([loc[0], loc[1]]):
1486
                    item.area = area.name
1487
                    break
1488

    
1489
            node = item.toXml()
1490

    
1491
            return node
1492
        except Exception as ex:
1493
            from App import App
1494
            from AppDocData import MessageType
1495

    
1496
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1497
                      f"{sys.exc_info()[-1].tb_lineno}"
1498
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1499

    
1500
    def lines_to_xml(self, layers: list, line_types: list, line_node) -> list:
1501
        """try to convert line element to id2 xml"""
1502
        from LineTypeConditions import LineTypeConditions
1503
        from EngineeringLineItem import QEngineeringLineItem
1504

    
1505
        def get_line_type(layers: list, line_types: list, layer_name: str, line_type: str) -> str:
1506
            """return line type"""
1507

    
1508
            '''
1509
            res = line_type
1510
            if line_type.upper() == 'ByLayer'.upper():
1511
                matches = [layer for layer in layers if layer[0] == layer_name]
1512
                if matches:
1513
                    line_type_oid = matches[0][1]
1514
                    matches = [line_node for line_node in line_types if line_node[0] == line_type_oid]
1515
                    if matches:
1516
                        res = matches[0][1]
1517
            '''
1518
            res = layer_name
1519

    
1520
            return res
1521

    
1522
        try:
1523
            pts = []
1524
            for vertex in line_node.iter('Vertex'):
1525
                pts.append(self.convert_to_image_coords([float(vertex.attrib['X']), float(vertex.attrib['Y'])]))
1526

    
1527
            # append first point if 'Closed' attribute is True
1528
            if 'Closed' in line_node.attrib and line_node.attrib['Closed'].upper() == 'True'.upper():
1529
                pts.append(pts[0])
1530

    
1531
            """get id2 line type uid"""
1532
            line_type, line_type_cond = get_line_type(layers, line_types, line_node.attrib['Layer'],
1533
                                                      line_node.attrib['Linetype']), None
1534
            model = self.ui.treeViewLineType.model()
1535
            for row in range(model.rowCount()):
1536
                parent_index = model.index(row, 0)
1537
                id2_line_type = model.itemFromIndex(parent_index).text()
1538

    
1539
                child_count = model.rowCount(parent_index)
1540
                for child_row in range(child_count):
1541
                    child_index = model.index(child_row, 1, parent_index)
1542
                    autocad_line_type = model.itemFromIndex(child_index).text()
1543
                    if autocad_line_type == line_type:
1544
                        matches = [item for item in LineTypeConditions.items() if item.name == id2_line_type]
1545
                        if matches:
1546
                            line_type_cond = matches[0]
1547
                        break
1548
            """up to here"""
1549

    
1550
            nodes = []
1551
            for idx in range(len(pts) - 1):
1552
                start, end = pts[idx], pts[idx + 1]
1553
                dx, dy = end[0] - start[0], end[1] - start[1]
1554

    
1555
                """check if length is zero"""
1556
                length = dx*dx + dy*dy
1557
                if length == 0:
1558
                    continue
1559

    
1560
                item = QEngineeringLineItem(vertices=[start, end], thickness=5)
1561

    
1562
                # import lines which is horizontal or vertical
1563
                #angle = math.degrees(item.angle())
1564
                #if not ((-5 < angle < 5) or (85 < angle < 95) or (175 < angle < 185) or (355 < angle < 365)):
1565
                #    continue
1566

    
1567
                item.area = 'Drawing'
1568

    
1569
                if line_type_cond:
1570
                    item.lineType = line_type_cond.name
1571
                    node = item.toXml()
1572
                    nodes.append(node)
1573

    
1574
            return nodes
1575
        except Exception as ex:
1576
            from App import App
1577
            from AppDocData import MessageType
1578

    
1579
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1580
                      f"{sys.exc_info()[-1].tb_lineno}"
1581
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1582

    
1583
        return None
1584

    
1585
def close(self):
1586
    QDialog.reject(self)
클립보드 이미지 추가 (최대 크기: 500 MB)