프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / ImportTextFromCADDialog.py @ 52437c65

이력 | 보기 | 이력해설 | 다운로드 (84.1 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
        #self._layer_names = sorted(layer_names)
26
        #self._layer_names.insert(0, '')
27

    
28
    def createEditor(self, parent, option, index):
29
        editor = None
30

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

    
37
        return editor if editor else super(LineTypeMappingDelegate, self).createEditor(parent, option, index)
38

    
39

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

    
44
        self._symbol_types = symbol_types
45

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

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

    
56
        return editor if editor else super(SymbolMappingDelegate, self).createEditor(parent, option, index)
57

    
58

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

    
66
        return ret
67

    
68

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

    
74
        QStandardItemModel.__init__(self)
75

    
76
        app_doc_data = AppDocData.instance()
77
        configs = app_doc_data.getConfigs(section='Line Type Mapping')
78

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

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

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

    
99

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

    
106
        self._autocad_symbols = []  # holder for autocad symbol
107

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

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

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

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

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

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

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

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

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

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

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

    
180
                    items[0].appendRow(childs)
181

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

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

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

    
197

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

    
206
        """constructor"""
207
        QStandardItemModel.__init__(self, parent=parent)
208

    
209
        self._autocad_symbols = []  # holder for autocad symbol
210

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

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

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

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

    
241

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

    
247
        self.ui = ImportTextFromCAD_UI.Ui_ImportTextFromCADDialog()
248
        self.ui.setupUi(self)
249

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

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

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

    
297
        self.text_scale = None
298
        self.id2_bbox = None
299
        self.scales = None
300
        self.offsets = None
301
        self.areas = None
302
        self.typicals = None
303

    
304
        self.isAccepted = False
305
 
306
        self.ui.toolButtonCAD.clicked.connect(self.on_add_cad_click)
307
        self.ui.pushButtonSave.clicked.connect(self.on_save_mappings)
308
        self.ui.pushButtonImport.clicked.connect(self.importClicked)
309
        self.ui.pushButtonClose.clicked.connect(self.close)
310
        self.ui.pushButtonAuto.clicked.connect(self.autoCalOffset)
311
        self.ui.checkBoxAuto.stateChanged.connect(self.autoStateChanged)
312
        self.ui.checkBoxLegend.stateChanged.connect(self.legendStateChanged)
313
        self.ui.checkBoxAuto.stateChanged.connect(self.autoStateChanged)
314
        self.ui.pushButtonAutoMapping.clicked.connect(self.autoMapping)
315
        self.ui.pushButtonRefresh.clicked.connect(self.symbolRefesh)
316

    
317
        configs = app_doc_data.getConfigs('Cad State', 'Auto')
318
        self.ui.checkBoxAuto.setChecked(bool(int((configs[0].value)))) if 1 == len(configs) else \
319
                self.ui.checkBoxAuto.setChecked(True)
320
        self.ui.checkBoxGenDrawing.setChecked(False)
321

    
322
        # disabel manual cal.
323
        '''
324
        self.ui.label.setVisible(False)
325
        self.ui.spinBoxX.setVisible(False)
326
        self.ui.spinBoxY.setVisible(False)
327
        self.ui.label_3.setVisible(False)
328
        self.ui.spinBoxTextX.setVisible(False)
329
        self.ui.spinBoxTextY.setVisible(False)
330
        self.ui.doubleSpinBoxScale.setVisible(False)
331
        self.ui.label_8.setVisible(False)
332
        self.ui.spinBoxSymbolX.setVisible(False)
333
        self.ui.spinBoxSymbolY.setVisible(False)
334
        self.ui.label_4.setVisible(False)
335
        self.ui.lineEdit.setVisible(False)
336
        self.ui.pushButtonAuto.setVisible(False)
337
        self.ui.label_5.setVisible(False)
338
        self.ui.lineEdit_2.setVisible(False)
339
        self.ui.label_6.setVisible(False)
340
        self.ui.doubleSpinBox.setVisible(False)
341
        self.ui.doubleSpinBox_2.setVisible(False)
342
        self.ui.checkBoxAuto.setVisible(False)
343
        '''
344

    
345
    def importClicked(self):
346
        if self.ui.checkBoxLegend.isChecked():
347
            self.on_import_legend()
348
        else:
349
            self.on_import_autocad()
350

    
351
    def legendStateChanged(self, checkState):
352
        if checkState is int(Qt.Checked):
353
            self.ui.checkBoxLine.setEnabled(False)
354
            self.ui.checkBoxSymbol.setEnabled(False)
355
            self.ui.checkBoxText.setEnabled(False)
356
        elif checkState is int(Qt.Unchecked):
357
            self.ui.checkBoxLine.setEnabled(True)
358
            self.ui.checkBoxSymbol.setEnabled(True)
359
            self.ui.checkBoxText.setEnabled(True)
360

    
361
    def autoStateChanged(self, checkState):
362
        if checkState is int(Qt.Checked):
363
            self.ui.spinBoxX.setEnabled(False)
364
            self.ui.spinBoxY.setEnabled(False)
365
            self.ui.spinBoxTextX.setEnabled(False)
366
            self.ui.spinBoxTextY.setEnabled(False)
367
            self.ui.spinBoxSymbolX.setEnabled(False)
368
            self.ui.spinBoxSymbolY.setEnabled(False)
369
            self.ui.doubleSpinBoxScale.setEnabled(False)
370
            self.ui.doubleSpinBox.setEnabled(False)
371
            self.ui.doubleSpinBox_2.setEnabled(False)
372
            self.ui.lineEdit.setEnabled(False)
373
            self.ui.lineEdit_2.setEnabled(False)
374
        elif checkState is int(Qt.Unchecked):
375
            self.ui.spinBoxX.setEnabled(True)
376
            self.ui.spinBoxY.setEnabled(True)
377
            self.ui.spinBoxTextX.setEnabled(True)
378
            self.ui.spinBoxTextY.setEnabled(True)
379
            self.ui.spinBoxSymbolX.setEnabled(True)
380
            self.ui.spinBoxSymbolY.setEnabled(True)
381
            self.ui.doubleSpinBoxScale.setEnabled(True)
382
            self.ui.doubleSpinBox.setEnabled(True)
383
            self.ui.doubleSpinBox_2.setEnabled(True)
384
            self.ui.lineEdit.setEnabled(True)
385
            self.ui.lineEdit_2.setEnabled(True)
386

    
387
    def symbolRefesh(self):
388
        self.on_load_symbol_mapping()
389

    
390
    def autoMapping(self):
391
        if not self._dwgs:
392
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file(s)'))
393
            return
394

    
395
        model = self.ui.treeViewSymbolMapping.model()
396
        row_index = []
397
        for row in range(model.rowCount()):
398
            category_index = model.index(row, 0)
399

    
400
            child_count = model.rowCount(category_index)
401
            for child_row in range(child_count):
402
                child_index = model.index(child_row, 0, category_index)
403
                id2_symbol_item = model.itemFromIndex(child_index)
404
                id2_symbol_uid = id2_symbol_item.data(Qt.UserRole)
405
                cad_symbol_list = [(symbol if '+' not in symbol else symbol.split('+')[1], symbol) for symbol in self._symbol_types]
406
                matches = [cad_symbol for cad_symbol in cad_symbol_list if cad_symbol[0].upper() == id2_symbol_item.text().upper()]
407
                if matches:
408
                    row_index.insert(0, [row, child_row, matches[0][1], id2_symbol_uid, id2_symbol_item])
409

    
410
        for categoty, child, symbol, uid, item in row_index:
411
            category_index = model.index(categoty, 0)
412
            child_index = model.index(child, 0, category_index)
413
            if symbol not in model._autocad_symbols:
414
                model._autocad_symbols.append(symbol)
415
                childs = [QStandardItem(''), QStandardItem(symbol), QStandardItem('')]
416
                childs[0].setData(uid, Qt.UserRole)
417
                for _child in childs:
418
                    _child.setEditable(False)
419
                item.appendRow(childs)
420

    
421
                button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
422
                button.setMaximumWidth(20)
423
                _index = model.index(model.rowCount(child_index) - 1, 2, child_index)
424
                button.clicked.connect(self.on_remove_symbol_mapping_item)
425
                self.ui.treeViewSymbolMapping.setIndexWidget(_index, button)                    
426

    
427
    def autoCalOffset(self):
428
        """ auto calculate offset """
429
        from App import App
430
        from EngineeringTextItem import QEngineeringTextItem
431

    
432
        if not self._dwgs:
433
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file(s)'))
434
            return
435

    
436
        # find ID2 Text Item
437
        find_text_1 = self.ui.lineEdit.text()
438
        find_text_2 = self.ui.lineEdit_2.text()
439

    
440
        matches_1 = [item for item in App.mainWnd().graphicsView.items() if issubclass(type(item), QEngineeringTextItem) and item.text() == find_text_1]
441
        matches_2 = [item for item in App.mainWnd().graphicsView.items() if issubclass(type(item), QEngineeringTextItem) and item.text() == find_text_2]
442
        if not matches_1 or not matches_2:
443
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no search text items.'))
444
            return
445
        
446
        text_item_1 = matches_1[0]
447
        text_loc_1 = text_item_1.loc
448
        text_item_2 = matches_2[0]
449
        text_loc_2 = text_item_2.loc
450

    
451
        cad_item_loc_1 = None
452
        cad_item_loc_2 = None
453
        # up to here
454

    
455
        app_doc_data = AppDocData.instance()
456
        project = app_doc_data.getCurrentProject()
457

    
458
        temp_path = project.getTempPath()
459
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
460
                         (os.path.splitext(f)[1].upper() == '.XML')]
461

    
462
        _file = self._dwgs[0]
463
        file_name = os.path.splitext(os.path.basename(_file))[0]
464
        autocad_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
465
        matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
466

    
467
        if matches:
468
            try:
469
                id2_xml_path = os.path.join(temp_path, matches[0])
470
                id2_xml = parse(id2_xml_path)
471
                id2_xml_root = id2_xml.getroot()
472

    
473
                autocad_xml = parse(autocad_xml_path)
474
                autocad_xml_root = autocad_xml.getroot()
475
                
476
                find_1 = False
477
                find_2 = False
478
                for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
479
                    if find_1 and find_2:
480
                        break
481

    
482
                    if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
483
                        continue
484
                    '''
485
                    min_values = [float(token) for token in
486
                                    blk_tbl_record.attrib['MinExtents'].strip('(').strip(')').split(',')]
487
                    max_values = [float(token) for token in
488
                                    blk_tbl_record.attrib['MaxExtents'].strip('(').strip(')').split(',')]
489
                    autocad_bbox = [min_values[0], min_values[1],
490
                                    max_values[0] - min_values[0], max_values[1] - min_values[1]]
491
                    '''
492
                    for text_node in blk_tbl_record.iter('AcDbText'):
493
                        if find_text_1 != text_node.text and find_text_2 != text_node.text:
494
                            continue
495
                        elif find_text_1 == text_node.text:
496
                            x = float(text_node.attrib['X'])
497
                            y = float(text_node.attrib['Y'])
498
                            h = float(text_node.attrib['Height'])
499
                            cad_item_loc_1 = [x, y, h]
500
                            find_1 = True
501
                        elif find_text_2 == text_node.text:
502
                            x = float(text_node.attrib['X'])
503
                            y = float(text_node.attrib['Y'])
504
                            h = float(text_node.attrib['Height'])
505
                            cad_item_loc_2 = [x, y, h]
506
                            find_2 = True
507

    
508
                    if not find_1 or not find_2:
509
                        for blk_ref in blk_tbl_record.iter('AcDbBlockReference'):
510
                            if find_1 and find_2:
511
                                break
512

    
513
                            for text_node in blk_ref.iter('AcDbAttribute'):
514
                                if find_text_1 != text_node.text and find_text_2 != text_node.text:
515
                                    continue
516
                                elif find_text_1 == text_node.text:
517
                                    x = float(text_node.attrib['X'])
518
                                    y = float(text_node.attrib['Y'])
519
                                    h = float(text_node.attrib['Height'])
520
                                    cad_item_loc_1 = [x, y, h]
521
                                    find_1 = True
522
                                elif find_text_2 == text_node.text:
523
                                    x = float(text_node.attrib['X'])
524
                                    y = float(text_node.attrib['Y'])
525
                                    h = float(text_node.attrib['Height'])
526
                                    cad_item_loc_2 = [x, y, h]
527
                                    find_2 = True
528
                    
529
                if find_1 and find_2:
530
                    dx_1 = abs(text_loc_1[0] - text_loc_2[0])
531
                    dx_2 = abs(cad_item_loc_1[0] - cad_item_loc_2[0])
532
                    scale_x = dx_1 / dx_2
533

    
534
                    dy_1 = abs(text_loc_1[1] - text_loc_2[1])
535
                    dy_2 = abs(cad_item_loc_1[1] - cad_item_loc_2[1])
536
                    scale_y = dy_1 / dy_2
537

    
538
                    offset_x = text_loc_1[0] - cad_item_loc_1[0] * scale_x
539
                    drawing_height = int(id2_xml_root.find('SIZE').text.split(',')[1])
540
                    offset_y = text_loc_1[1] - (drawing_height - cad_item_loc_1[1] * scale_y) + cad_item_loc_1[2] * scale_y
541

    
542
                    self.ui.spinBoxTextX.setValue(offset_x)
543
                    self.ui.spinBoxX.setValue(offset_x)
544
                    self.ui.spinBoxSymbolX.setValue(offset_x)
545
                    self.ui.spinBoxTextY.setValue(offset_y)
546
                    self.ui.spinBoxY.setValue(offset_y)
547
                    self.ui.spinBoxSymbolY.setValue(offset_y)
548
                    self.ui.doubleSpinBox.setValue(scale_x)
549
                    self.ui.doubleSpinBox_2.setValue(scale_y)
550

    
551
            except Exception as ex:
552
                from App import App
553
                from AppDocData import MessageType
554

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

    
559
    def on_load_line_type_mapping(self):
560
        """load ID2-AutoCAD """
561
        def on_remove_treeview_item():
562
            indices = self.ui.treeViewLineType.selectionModel().selectedRows()
563
            for index in sorted(indices):
564
                self.ui.treeViewLineType.model().removeRow(index.row(), index.parent())
565

    
566
        def on_add_line_type_item(index: QModelIndex):
567
            """add AutoCAD line type corresponding to ID2 line type"""
568

    
569
            try:
570
                _index = self.ui.treeViewLineType.model().index(index.row(), 1)
571
                autocad_line_type_item = self.ui.treeViewLineType.model().itemFromIndex(_index)
572
                if autocad_line_type_item and autocad_line_type_item.text():
573
                    _index = self.ui.treeViewLineType.model().index(index.row(), 0, index.parent())
574
                    id2_line_type_item = self.ui.treeViewLineType.model().itemFromIndex(_index)
575
                    items = [QStandardItem(''), QStandardItem(autocad_line_type_item.text()), QStandardItem('')]
576
                    items[0].setData(id2_line_type_item.text(), Qt.UserRole)
577
                    for item in items:
578
                        item.setEditable(False)
579
                    id2_line_type_item.appendRow(items)
580

    
581
                    ## add remove button
582
                    child_count = self.ui.treeViewLineType.model().rowCount(id2_line_type_item.index())
583
                    button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
584
                    button.setMaximumWidth(20)
585
                    button.clicked.connect(on_remove_treeview_item)
586
                    _index = self.ui.treeViewLineType.model().index(child_count - 1, 2, id2_line_type_item.index())
587
                    self.ui.treeViewLineType.setIndexWidget(_index, button)
588

    
589
                    self.ui.treeViewLineType.expandAll()
590
            except Exception as ex:
591
                from App import App
592
                from AppDocData import MessageType
593

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

    
598
        model = LineTypeMappingModel()
599
        model.invisibleRootItem()
600
        self.ui.treeViewLineType.setModel(model)
601
        self.ui.treeViewLineType.setItemDelegate(LineTypeMappingDelegate(self._layer_names, self.ui.treeViewLineType))
602

    
603
        for row in range(model.rowCount()):
604
            button = QPushButton(icon=QIcon(":/newPrefix/Add.svg"))
605
            button.setMaximumWidth(20)
606
            index = self.ui.treeViewLineType.model().index(row, 2)
607
            button.clicked.connect(partial(on_add_line_type_item, index))
608
            self.ui.treeViewLineType.setIndexWidget(index, button)
609

    
610
            parent_index = model.index(row, 0)
611
            child_count = model.rowCount(parent_index)
612
            for child_row in range(child_count):
613
                button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
614
                button.setMaximumWidth(20)
615
                _index = self.ui.treeViewLineType.model().index(child_row, 2, parent_index)
616
                button.clicked.connect(on_remove_treeview_item)
617
                self.ui.treeViewLineType.setIndexWidget(_index, button)
618

    
619
        self.ui.treeViewLineType.resizeColumnToContents(0)
620
        self.ui.treeViewLineType.resizeColumnToContents(1)
621
        self.ui.treeViewLineType.resizeColumnToContents(2)
622
        self.ui.treeViewLineType.expandAll()
623

    
624
        self.ui.label.setHidden(True)
625
        self.ui.spinBoxX.setHidden(True)
626
        self.ui.spinBoxY.setHidden(True)
627
        self.ui.label_8.setHidden(True)
628
        self.ui.spinBoxSymbolX.setHidden(True)
629
        self.ui.spinBoxSymbolY.setHidden(True)
630
        self.ui.label_3.setText('Offset(x, y, text scale) : ')
631

    
632
    def on_remove_symbol_mapping_item(self):
633
        """remove selected items"""
634
        indices = self.ui.treeViewSymbolMapping.selectionModel().selectedRows()
635
        for index in sorted(indices):
636
            _index = self.ui.treeViewSymbolMapping.model().index(index.row(), 1, index.parent())
637
            id2_symbol_item = self.ui.treeViewSymbolMapping.model().itemFromIndex(_index)
638
            self.ui.treeViewSymbolMapping.model()._autocad_symbols.remove(id2_symbol_item.text())
639

    
640
            self.ui.treeViewSymbolMapping.model().removeRow(index.row(), index.parent())
641

    
642
    def on_add_symbol_type_item(self, index: QModelIndex):
643
        """map AutoCAD symbol and ID2 symbol"""
644

    
645
        try:
646
            _index = self.ui.treeViewSymbolMapping.model().index(index.row(), 1, index.parent())
647
            autocad_symbol_item = self.ui.treeViewSymbolMapping.model().itemFromIndex(_index)
648
            if autocad_symbol_item and autocad_symbol_item.text():
649
                _index = self.ui.treeViewSymbolMapping.model().index(index.row(), 0, index.parent())
650
                id2_symbol_item = self.ui.treeViewSymbolMapping.model().itemFromIndex(_index)
651
                if autocad_symbol_item.text() not in self.ui.treeViewSymbolMapping.model()._autocad_symbols:
652
                    items = [QStandardItem(''), QStandardItem(autocad_symbol_item.text()), QStandardItem('')]
653
                    items[0].setData(id2_symbol_item.data(Qt.UserRole), Qt.UserRole)
654
                    for item in items:
655
                        item.setEditable(False)
656
                    id2_symbol_item.appendRow(items)
657

    
658
                    self.ui.treeViewSymbolMapping.model()._autocad_symbols.append(autocad_symbol_item.text())
659

    
660
                    ## add remove button
661
                    child_count = self.ui.treeViewSymbolMapping.model().rowCount(id2_symbol_item.index())
662
                    button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
663
                    button.setMaximumWidth(20)
664
                    button.clicked.connect(self.on_remove_symbol_mapping_item)
665
                    _index = self.ui.treeViewSymbolMapping.model().index(child_count - 1, 2, id2_symbol_item.index())
666
                    self.ui.treeViewSymbolMapping.setIndexWidget(_index, button)
667

    
668
                    self.ui.treeViewSymbolMapping.expandAll()
669
        except Exception as ex:
670
            from App import App
671
            from AppDocData import MessageType
672

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

    
677
    def on_load_symbol_mapping(self):
678
        """
679
        load symbol mapping setting
680
        """
681

    
682
        model = SymbolMappingModel()
683
        model.invisibleRootItem()
684
        self.ui.treeViewSymbolMapping.setModel(model)
685

    
686
        for row in range(model.rowCount()):
687
            parent_index = model.index(row, 0)
688
            child_count = model.rowCount(parent_index)
689
            for child_row in range(child_count):
690
                button = QPushButton(icon=QIcon(":/newPrefix/Add.svg"))
691
                button.setMaximumWidth(20)
692
                index = model.index(child_row, 2, parent_index)
693
                button.clicked.connect(partial(self.on_add_symbol_type_item, index))
694
                self.ui.treeViewSymbolMapping.setIndexWidget(index, button)
695

    
696
                """add autocad symbol item"""
697
                id2_symbol_index = model.index(child_row, 0, parent_index)
698
                acad_symbol_count = model.rowCount(id2_symbol_index)
699
                for acad_symbol in range(acad_symbol_count):
700
                    button = QPushButton(icon=QIcon(":/newPrefix/Remove.svg"))
701
                    button.setMaximumWidth(20)
702
                    _index = model.index(acad_symbol, 2, id2_symbol_index)
703
                    button.clicked.connect(self.on_remove_symbol_mapping_item)
704
                    self.ui.treeViewSymbolMapping.setIndexWidget(_index, button)
705

    
706
        self.ui.treeViewSymbolMapping.resizeColumnToContents(0)
707
        self.ui.treeViewSymbolMapping.resizeColumnToContents(1)
708
        self.ui.treeViewSymbolMapping.resizeColumnToContents(2)
709
        self.ui.treeViewSymbolMapping.expandAll()
710

    
711
        self._symbol_types = self._symbol_types +  model.autocad_symbols[:]
712
        self.ui.treeViewSymbolMapping.setItemDelegate(SymbolMappingDelegate(self._symbol_types,
713
                                                                            self.ui.treeViewSymbolMapping))
714

    
715
    def on_load_exclude_layers_mapping(self) -> None:
716
        """
717
        load exclude layers mapping
718
        :return:
719
        """
720
        model = ExcludeLayersModel(self)
721
        self.ui.listViewExcludeLayers.setModel(model)
722

    
723
    def on_add_cad_click(self):
724
        """
725
        extract information from select autocad files
726
        :return:
727
        """
728
        from App import App 
729

    
730
        mainWnd = App.mainWnd()
731
        project = AppDocData.instance().getCurrentProject()
732
        drawing_path = project.getDrawingFilePath()
733

    
734
        options = QFileDialog.Options()
735
        options |= QFileDialog.DontUseNativeDialog
736
        files, _ = QFileDialog.getOpenFileNames(self, self.tr('Import', "Select AutoCAD File"),
737
                                                os.path.join(drawing_path, 'Native'),
738
                                                "dwg files (*.dwg)", options=options)
739
        if files:
740
            self.ui.lineEditCAD.setText(files[0]) if len(files) == 1 else \
741
                self.ui.lineEditCAD.setText(str(len(files)) + ' files')
742
            
743
            mainWnd.progress_bar.setMaximum(len(files) + 1)
744
            count = 1
745
            mainWnd.progress_bar.setValue(count)
746

    
747
            self._dwgs.clear()
748
            self._line_types.clear()
749
            self._symbol_types.clear()
750
            self._symbol_types.append('')
751
            self._layer_names.clear()
752
            self._layer_names.append('')
753
            for _file in files:
754
                if os.path.exists(_file):
755
                    try:
756
                        file = os.path.normpath(_file)
757
                        drawing_name = os.path.splitext(os.path.basename(file))[0]
758
                        executable = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'OdReadExMgd',
759
                                                  'OdReadExMgd.exe')
760
                        args = [executable, file, '0', '0', '1', '1' if int(self.ui.checkBoxGenDrawing.checkState()) is int(Qt.Checked) else '0']
761
                        CREATE_NO_WINDOW = 0x08000000
762
                        p = subprocess.Popen(
763
                            args,
764
                            stdin=subprocess.PIPE,
765
                            stdout=subprocess.PIPE,
766
                            stderr=subprocess.PIPE,
767
                            creationflags=CREATE_NO_WINDOW
768
                        )
769

    
770
                        stdout, stderr = p.communicate()
771
                        if len(stderr) != 0 or ('eFileSharingViolation' in str(stdout) or 300 > len(stdout)):
772
                            #raise RuntimeError('OdReadExMgd threw error:\n' + stderr.decode('utf-8'))
773
                            raise RuntimeError('OdReadExMgd threw error in ' + drawing_name + ' :\n' + str(stderr)) if len(stderr) != 0 else \
774
                                RuntimeError('OdReadExMgd threw error in ' + drawing_name + ' :\n' + str(stdout))
775

    
776
                        """load content size and image for graphic"""
777
                        drawing_file_path = os.path.join(drawing_path, drawing_name + '.PNG')
778
                        if not os.path.exists(drawing_file_path):
779
                            continue
780
                        drawing = Drawing(str(uuid.uuid4()), drawing_name + ".PNG", None)
781
                        #_image = drawing.image
782
                        #bytesPerLine = _image.shape[1]
783
                        #image = QImage(_image.data, _image.shape[1], _image.shape[0], bytesPerLine, QImage.Format_Indexed8)
784
                        #self.id2_bbox = self.get_contents_size_from_image(drawing_file_path)
785
                        self.id2_bbox = drawing.get_contents_size_from_image()
786
                        """up to here"""
787

    
788
                        """parse layer, line type and symbol"""
789
                        autocad_xml_path = os.path.join(os.path.dirname(file), drawing_name + '.xml')
790
                        autocad_xml = parse(autocad_xml_path)
791
                        autocad_xml_root = autocad_xml.getroot()
792

    
793
                        for layer_tbl_record in autocad_xml_root.iter('AcDbLayerTableRecord'):
794
                            layer_name = layer_tbl_record.attrib['Name']
795
                            if layer_name in self._layer_names:
796
                                continue
797

    
798
                            self._layer_names.append(layer_name)
799

    
800
                        for line_type_tbl_record in autocad_xml_root.iter('AcDbLinetypeTableRecord'):
801
                            line_type_name = line_type_tbl_record.attrib['Name']
802
                            if line_type_name.upper() in ['ByBlock'.upper(), 'ByLayer'.upper()] or line_type_name in self._line_types:
803
                                continue
804

    
805
                            self._line_types.append(line_type_tbl_record.attrib['Name'])
806

    
807
                        for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
808
                            if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
809
                                continue
810

    
811
                            min_values = [float(token) for token in
812
                                        blk_tbl_record.attrib['MinExtents'].strip('()').split(',')]
813
                            max_values = [float(token) for token in
814
                                        blk_tbl_record.attrib['MaxExtents'].strip('()').split(',')]
815
                            autocad_bbox = [min_values[0], min_values[1],
816
                                            max_values[0] - min_values[0], max_values[1] - min_values[1]]
817

    
818
                            if self.ui.checkBoxAuto.isChecked():
819
                                scale_x = self.id2_bbox[2] / autocad_bbox[2]
820
                                scale_y = self.id2_bbox[3] / autocad_bbox[3]
821
                                self.scales = [scale_x, scale_y]
822
                                offsets = [self.id2_bbox[0] - autocad_bbox[0] * scale_x, self.id2_bbox[3] + self.id2_bbox[1]]
823
                                self.offsets = offsets + [autocad_bbox[1]]
824
                            else:
825
                                self.scales = [self.ui.doubleSpinBox.value(), self.ui.doubleSpinBox_2.value()]
826
                                self.offsets = [self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), self.id2_bbox[5]]
827

    
828
                        for block_ref_record in autocad_xml_root.iter('AcDbBlockReference'):
829
                            block_name = block_ref_record.attrib['Name']
830
                            if block_name in self._symbol_types:
831
                                continue
832
                            
833
                            self._symbol_types.append(block_name)
834

    
835
                            '''save graphic'''
836
                            #node = self.symbol_info(block_ref_record)
837
                            #if node and block_name.split('+')[0] == 'Graphic':
838
                            #    symbolImage = image.copy(round(node[2][0]), round(node[2][1]), math.ceil(node[3] + 2), math.ceil(node[4] + 1))
839
                            #    file_name = drawing_name + '++' + block_name + '++' + str(round(node[2][0])) + '++' + str(round(node[2][1]))
840
                            #    image_file_path = os.path.join(project.getGraphicFilePath(), file_name + '.PNG')
841
                            #    symbolImage.save(image_file_path, 'PNG')
842
                            '''up to here'''
843
                        """up to here"""
844

    
845
                        self._dwgs.append(file)
846

    
847
                        count += 1
848
                        mainWnd.progress_bar.setValue(count)
849
                        QApplication.processEvents()
850
                    except Exception as ex:
851
                        from App import App
852
                        from AppDocData import MessageType
853

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

    
858
            self.ui.listViewExcludeLayers.model().append_layers(self._layer_names)
859
            mainWnd.progress_bar.setValue(mainWnd.progress_bar.maximum())
860

    
861
            if self.ui.checkBoxGenDrawing.checkState():
862
                mainWnd.load_drawing_list()
863

    
864
            self._symbol_types.sort()
865
            self._layer_names.sort()
866

    
867
            QMessageBox.information(self, self.tr('Information'), self.tr('Successfully loaded'))
868

    
869
            return self._symbol_types
870

    
871
    def on_save_mappings(self):
872
        """save line type and symbol mappings"""
873
        import io
874
        import csv
875
        from AppDocData import Config
876

    
877
        configs = []
878
        configs.append(Config('Cad Offset', 'X', self.ui.spinBoxX.value()))
879
        configs.append(Config('Cad Offset', 'Y', self.ui.spinBoxY.value()))
880
        configs.append(Config('Cad Offset', 'Text X', self.ui.spinBoxTextX.value()))
881
        configs.append(Config('Cad Offset', 'Text Y', self.ui.spinBoxTextY.value()))
882
        configs.append(Config('Cad Offset', 'Scale', self.ui.doubleSpinBoxScale.value()))
883
        configs.append(Config('Cad Offset', 'X Scale', self.ui.doubleSpinBox.value()))
884
        configs.append(Config('Cad Offset', 'Y Scale', self.ui.doubleSpinBox_2.value()))
885
        configs.append(Config('Cad State', 'Auto', int(self.ui.checkBoxAuto.isChecked())))
886
        configs.append(Config('Cad State', 'Diagonal', int(self.ui.checkBoxDiagonal.isChecked())))
887
        configs.append(Config('Cad State', 'Text Overwrite', int(self.ui.checkBoxTextOverwrite.isChecked())))
888
        configs.append(Config('Cad State', 'Line', int(self.ui.checkBoxLine.isChecked())))
889
        configs.append(Config('Cad State', 'Text', int(self.ui.checkBoxText.isChecked())))
890
        configs.append(Config('Cad State', 'Symbol', int(self.ui.checkBoxSymbol.isChecked())))
891

    
892
        model = self.ui.treeViewLineType.model()
893
        for row in range(model.rowCount()):
894
            parent_index = model.index(row, 0)
895
            id2_line_type = model.itemFromIndex(parent_index).text()
896

    
897
            autocad_line_types = []
898
            child_count = model.rowCount(parent_index)
899
            for child_row in range(child_count):
900
                child_index = model.index(child_row, 1, parent_index)
901
                autocad_line_types.append(model.itemFromIndex(child_index).text())
902

    
903
            if autocad_line_types:
904
                configs.append(Config('Line Type Mapping', id2_line_type, ','.join(autocad_line_types)))
905

    
906
        # save symbol mappings
907
        model = self.ui.treeViewSymbolMapping.model()
908
        for row in range(model.rowCount()):
909
            category_index = model.index(row, 0)
910

    
911
            child_count = model.rowCount(category_index)
912
            for child_row in range(child_count):
913
                autocad_symbols = []
914
                child_index = model.index(child_row, 0, category_index)
915
                id2_symbol_item = model.itemFromIndex(child_index)
916
                id2_symbol_uid = id2_symbol_item.data(Qt.UserRole)
917
                id2_symbol = id2_symbol_item.text()
918
                mapping_count = model.rowCount(child_index)
919
                for mapping_row in range(mapping_count):
920
                    mapping_index = model.index(mapping_row, 1, child_index)
921
                    autocad_symbols.append(model.itemFromIndex(mapping_index).text())
922

    
923
                if autocad_symbols:
924
                    configs.append(Config('Symbol Mapping', id2_symbol_uid, ','.join(autocad_symbols)))
925

    
926
        # save exclude layers mappings
927
        model = self.ui.listViewExcludeLayers.model()
928
        exclude_layers = []
929
        for row in range(model.rowCount()):
930
            item = model.itemFromIndex(model.index(row, 0))
931
            if item.checkState() == Qt.Checked:
932
                exclude_layers.append(item.text())
933

    
934
        if exclude_layers:
935
            stream = io.StringIO()
936
            csv.writer(stream, delimiter=',', escapechar='^', quoting=csv.QUOTE_NONE).writerow(exclude_layers)
937
            configuration = stream.getvalue()
938

    
939
            configs.append(Config('Exclude Layers Mapping', 'All', configuration))
940

    
941
        app_doc_data = AppDocData.instance()
942
        """delete line type mapping and symbol mapping"""
943
        app_doc_data.deleteConfigs(section='Line Type Mapping')
944
        app_doc_data.deleteConfigs(section='Symbol Mapping')
945
        app_doc_data.deleteConfigs(section='Exclude Layers Mapping')
946

    
947
        app_doc_data.saveConfigs(configs)
948

    
949
        QMessageBox.information(self, self.tr('Information'), self.tr('Successfully saved.'))
950
        """up to here"""
951

    
952
    def on_import_legend(self):
953
        import XmlGenerator as xg
954
        from App import App
955

    
956
        if not self._dwgs:
957
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file'))
958
            return
959

    
960
        mainWnd = App.mainWnd()
961
        app_doc_data = AppDocData.instance()
962
        project = app_doc_data.getCurrentProject()
963

    
964
        temp_path, drawing_path = project.getTempPath(), project.getDrawingFilePath()
965
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
966
                         (os.path.splitext(f)[1].upper() == '.XML')]
967
        
968
        mainWnd.progress_bar.setMaximum(len(self._dwgs) + 1)
969
        count = 1
970
        mainWnd.progress_bar.setValue(count)
971

    
972
        create = False
973

    
974
        for _file in self._dwgs:
975
            file_name = os.path.splitext(os.path.basename(_file))[0]
976
            id2_image_file = os.path.join(drawing_path, file_name + '.png')
977
            autocad_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
978
            matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
979

    
980
            if not matches:
981
                path = os.path.join(app_doc_data.getCurrentProject().getTempPath(), file_name + '.xml')
982
                img = AppDocData.my_imread(file_path=id2_image_file)
983
                _width = img.shape[1]
984
                _height = img.shape[0]
985
                xml, result = xg.write_to_xml(path, _width, _height, [], [])
986
                if xml:
987
                    from xml.etree import ElementTree
988
                    ElementTree.ElementTree(xml).write(path)
989
                    matches = [file_name + '.xml']
990
        
991
            if matches and os.path.exists(id2_image_file):
992
                try:
993
                    symbols = {}
994

    
995
                    drawing = Drawing(str(uuid.uuid4()), file_name + ".PNG", None)
996
                    _image = drawing.image#QImage(id2_image_file, "PNG")
997
                    bytesPerLine = _image.shape[1]
998
                    image = QImage(_image.data, _image.shape[1], _image.shape[0], bytesPerLine, QImage.Format_Indexed8)
999
                    autocad_xml = parse(autocad_xml_path)
1000

    
1001
                    autocad_xml_root = autocad_xml.getroot()
1002

    
1003
                    #self.id2_bbox = self.get_contents_size_from_image(id2_image_file)
1004
                    self.id2_bbox = drawing.get_contents_size_from_image()
1005

    
1006
                    for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
1007
                        if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper():
1008
                            continue
1009

    
1010
                        min_values = [float(token) for token in
1011
                                      blk_tbl_record.attrib['MinExtents'].strip('()').split(',')]
1012
                        max_values = [float(token) for token in
1013
                                      blk_tbl_record.attrib['MaxExtents'].strip('()').split(',')]
1014
                        autocad_bbox = [min_values[0], min_values[1],
1015
                                        max_values[0] - min_values[0], max_values[1] - min_values[1]]
1016

    
1017
                        if self.ui.checkBoxAuto.isChecked():
1018
                            scale_x = self.id2_bbox[2] / autocad_bbox[2]
1019
                            scale_y = self.id2_bbox[3] / autocad_bbox[3]
1020
                            self.scales = [scale_x, scale_y]
1021
                            offsets = [self.id2_bbox[0] - autocad_bbox[0] * scale_x, self.id2_bbox[3] + self.id2_bbox[1]]
1022
                            self.offsets = offsets + [autocad_bbox[1]]
1023
                        else:
1024
                            self.scales = [self.ui.doubleSpinBox.value(), self.ui.doubleSpinBox_2.value()]
1025
                            self.offsets = [self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), self.id2_bbox[5]]
1026

    
1027
                        for blk_ref in blk_tbl_record.iter('AcDbBlockReference'):
1028
                            node = self.symbol_info(blk_ref)
1029
                            if node and node[0] not in symbols:
1030
                                symbols[node[0]] = node
1031

    
1032
                    dialog = QSymbolRegiDataListDialog(self, image, symbols)
1033
                    if dialog.showDialog():
1034
                        create = True
1035
                    
1036
                    '''
1037
                    for key, value in symbols.items():
1038
                        symbolEditorDialog = QSymbolEditorDialog(self, image.copy(math.floor(value[2][0]), math.floor(value[2][1]), \
1039
                                                                    math.ceil(value[3] + 1), math.ceil(value[4] + 1)), \
1040
                                                                    AppDocData.instance().getCurrentProject(), value[1], False)
1041
                        (isAccepted, _, _, _, _) = symbolEditorDialog.showDialog()
1042

1043
                        if not isAccepted:
1044
                            reply = QMessageBox.question(self, self.tr('Stop?'), self.tr('Do you want to stop registering symbols?'), QMessageBox.Yes, QMessageBox.Cancel)
1045
                            if reply == QMessageBox.Yes:
1046
                                break
1047
                    '''
1048

    
1049
                except Exception as ex:
1050
                    from App import App 
1051
                    from AppDocData import MessageType
1052

    
1053
                    message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1054
                              f"{sys.exc_info()[-1].tb_lineno}"
1055
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1056
            else:
1057
                from App import App
1058
                from AppDocData import MessageType
1059

    
1060
                message = 'There is no xml file'
1061
                App.mainWnd().addMessage.emit(MessageType.Error, message)
1062

    
1063
            count += 1
1064
            mainWnd.progress_bar.setValue(count)
1065
            QApplication.processEvents()
1066

    
1067
        mainWnd.progress_bar.setValue(mainWnd.progress_bar.maximum())
1068
        if create:
1069
            App.mainWnd().symbolTreeWidget.initSymbolTreeView()
1070
            QMessageBox.information(self, self.tr('Information'), self.tr('Symbol creation complete. Please click "Symbol Refresh" Button'))
1071

    
1072
    def on_import_autocad(self):
1073
        """
1074
        import line, text and symbol from autocad
1075
        @return: None
1076
        """
1077
        from App import App 
1078
        from Area import Area
1079
        import XmlGenerator as xg
1080

    
1081
        if not self._dwgs:
1082
            QMessageBox.information(self, self.tr('Information'), self.tr('There is no selected file(s)'))
1083
            return
1084

    
1085
        mainWnd = App.mainWnd()
1086
        app_doc_data = AppDocData.instance()
1087
        project = app_doc_data.getCurrentProject()
1088

    
1089
        temp_path, drawing_path = project.getTempPath(), project.getDrawingFilePath()
1090
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
1091
                         (os.path.splitext(f)[1].upper() == '.XML')]
1092

    
1093
        self.text_scale = self.ui.doubleSpinBoxScale.value()
1094
        self.areas = app_doc_data.getAreaList()
1095
        typical_list = app_doc_data.getConfigs('Global Typical Area')
1096
        self.typicals = []
1097
        if typical_list:
1098
            for typical_area in typical_list:
1099
                area = Area(typical_area.key)
1100
                area.parse(typical_area.value)
1101
                self.typicals.append(area)
1102
        title_area_list = app_doc_data.getTitleBlockProperties()
1103
        title_list = []
1104
        if title_area_list:
1105
            for title_area in title_area_list:
1106
                area = Area(title_area[0])
1107
                area.parse(title_area[2])
1108
                title_list.append(area)
1109
        self.areas.extend(title_list)
1110

    
1111
        """get exclude layers mapping"""
1112
        exclude_layers = []
1113
        model = self.ui.listViewExcludeLayers.model()
1114
        for row in range(model.rowCount()):
1115
            item = model.itemFromIndex(model.index(row, 0))
1116
            if item.checkState() == Qt.Checked:
1117
                exclude_layers.append(item.text())
1118

    
1119
        """get items to be converted"""
1120
        will_be_converted_items = []
1121
        if self.ui.checkBoxLine.checkState() == Qt.Checked:
1122
            will_be_converted_items.append('Line')
1123
        if self.ui.checkBoxText.checkState() == Qt.Checked:
1124
            will_be_converted_items.append('Text')
1125
        if self.ui.checkBoxSymbol.checkState() == Qt.Checked:
1126
            will_be_converted_items.append('Symbol')
1127

    
1128
        mapping_configs = app_doc_data.getConfigs(section='Symbol Mapping')
1129

    
1130
        mainWnd.progress_bar.setMaximum(len(self._dwgs) + 1)
1131
        count = 1
1132
        mainWnd.progress_bar.setValue(count)
1133

    
1134
        for _file in self._dwgs:
1135
            file_name = os.path.splitext(os.path.basename(_file))[0]
1136
            id2_image_file = os.path.join(drawing_path, file_name + '.png')
1137
            autocad_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
1138
            matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
1139

    
1140
            if not matches:
1141
                path = os.path.join(temp_path, file_name + '.xml')
1142
                img = AppDocData.my_imread(file_path=id2_image_file)
1143
                _width = img.shape[1]
1144
                _height = img.shape[0]
1145
                xml, result = xg.write_to_xml(path, _width, _height, [], [])
1146
                if xml:
1147
                    from xml.etree import ElementTree
1148
                    ElementTree.ElementTree(xml).write(path)
1149
                    matches = [file_name + '.xml']
1150

    
1151
            if matches and os.path.exists(id2_image_file):
1152
                try:
1153
                    layers, line_types, symbols = [], [], []
1154
                    autocad_xml = parse(autocad_xml_path)
1155

    
1156
                    """parse layer, line type and symbol"""
1157
                    autocad_xml_root = autocad_xml.getroot()
1158
                    for layer_tbl_record in autocad_xml_root.iter('AcDbLayerTableRecord'):
1159
                        layer_name = layer_tbl_record.attrib['Name']
1160
                        line_type_oid = layer_tbl_record.attrib['LinetypeObjectId']
1161
                        layers.append([layer_name, line_type_oid])
1162

    
1163
                    for line_type_tbl_record in autocad_xml_root.iter('AcDbLinetypeTableRecord'):
1164
                        line_type_oid = line_type_tbl_record.attrib['ObjectId']
1165
                        line_type_name = line_type_tbl_record.attrib['Name']
1166
                        line_types.append([line_type_oid, line_type_name])
1167

    
1168
                    '''
1169
                    for blk_ref_record in autocad_xml_root.iter('AcDbBlockReference'):
1170
                        blk_ref_handle = blk_ref_record.attrib['Handle']
1171
                        blk_ref_name = blk_ref_record.attrib['Name']
1172
                        symbols.append([blk_ref_handle, blk_ref_name])
1173
                    '''
1174
                    """up to here"""
1175

    
1176
                    id2_xml_path = os.path.join(temp_path, matches[0])
1177
                    id2_xml = parse(id2_xml_path)
1178
                    id2_xml_root = id2_xml.getroot()
1179

    
1180
                    #id2_bbox = [0, 0, 9600, 6781]
1181
                    drawing = Drawing(str(uuid.uuid4()), file_name + ".PNG", None)
1182
                    #self.id2_bbox = self.get_contents_size_from_image(id2_image_file)
1183
                    self.id2_bbox = drawing.get_contents_size_from_image()
1184

    
1185
                    content_size_node = id2_xml_root.find(xg.ROOT_CONTENT_SIZE)
1186
                    if content_size_node is None:
1187
                        SubElement(id2_xml_root, xg.ROOT_CONTENT_SIZE).text = ','.join([str(num) for num in self.id2_bbox[:-2]])
1188
                    elif not content_size_node.text:
1189
                        content_size_node.text = ','.join([str(num) for num in self.id2_bbox[:-2]])
1190

    
1191
                    if 'Symbol' in will_be_converted_items:
1192
                        symbols_node = id2_xml_root.find(xg.SYMBOL_LIST_NODE_NAME)
1193
                        if symbols_node:
1194
                            symbols = symbols_node.findall('SYMBOL')
1195
                            # remove only converted symbol nodes
1196
                            for symbol in symbols:
1197
                                if 'Converted' in symbol.attrib and symbol.attrib['Converted'] == str(True):
1198
                                    symbols_node.remove(symbol)
1199
                        graphics_node = id2_xml_root.find(xg.GRAPHIC_NODE_NAME)
1200
                        graphics_node.clear()
1201

    
1202
                    if 'Text' in will_be_converted_items:
1203
                        """remove texts from id2 xml file"""
1204
                        textInfo = id2_xml_root.find(xg.TEXT_INFO_LIST_NODE_NAME)
1205
                        if self.ui.checkBoxTextOverwrite.isChecked():
1206
                            textInfo.clear()
1207

    
1208
                        noteInfo = id2_xml_root.find(xg.NOTE_TEXT_INFO_LIST_NOTE_NAME)
1209
                        noteInfo.clear()
1210

    
1211
                        lineNoInfo = id2_xml_root.find(xg.LINE_NOS_NODE_NAME)
1212
                        if self.ui.checkBoxTextOverwrite.isChecked():
1213
                            lineNoInfo.clear()
1214
                        """up to here"""
1215

    
1216
                    if 'Line' in will_be_converted_items:
1217
                        """remove lines from id2 xml file"""
1218
                        line_infos = id2_xml_root.find(xg.LINE_INFOS_NODE_NAME)
1219
                        line_infos.clear()
1220
                        """up to here"""
1221

    
1222
                    """remove unknowns from id2 xml file"""
1223
                    unknowns = id2_xml_root.find(xg.UNKNOWNS_NODE_NAME)
1224
                    unknowns.clear()
1225
                    """up to here"""
1226

    
1227
                    """add text, line and symbol from autocad file to id2 xml file"""
1228
                    #if 'Text' in will_be_converted_items: # text for attrdef
1229
                    #    for record in autocad_xml_root.iter('AcDbAttributeDefinition'):
1230
                    #        for record in autocad_xml_root.iter('AcDbText'):
1231
                    #            if record.attrib['Layer'] not in exclude_layers:
1232
                    #                node = self.text_to_xml(record)
1233
                    #                if node:
1234
                    #                    textInfo.append(node)
1235

    
1236
                    for blk_tbl_record in autocad_xml_root.iter('AcDbBlockTableRecord'):
1237
                        if blk_tbl_record.attrib['Name'].upper() != '*Model_Space'.upper() or \
1238
                            blk_tbl_record.attrib['Name'].upper().startswith('graphic+'.upper()):
1239
                            continue
1240

    
1241
                        min_values = [float(token) for token in
1242
                                      blk_tbl_record.attrib['MinExtents'].strip('()').split(',')]
1243
                        max_values = [float(token) for token in
1244
                                      blk_tbl_record.attrib['MaxExtents'].strip('()').split(',')]
1245
                        autocad_bbox = [min_values[0], min_values[1],
1246
                                        max_values[0] - min_values[0], max_values[1] - min_values[1]]
1247

    
1248
                        if self.ui.checkBoxAuto.isChecked():
1249
                            scale_x = self.id2_bbox[2] / autocad_bbox[2]
1250
                            scale_y = self.id2_bbox[3] / autocad_bbox[3]
1251
                            self.scales = [scale_x, scale_y]
1252
                            offsets = [self.id2_bbox[0] - autocad_bbox[0] * scale_x, self.id2_bbox[3] + self.id2_bbox[1]]
1253
                            self.offsets = offsets + [autocad_bbox[1]]
1254
                        else:
1255
                            self.scales = [self.ui.doubleSpinBox.value(), self.ui.doubleSpinBox_2.value()]
1256
                            self.offsets = [self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), self.id2_bbox[5]]
1257

    
1258
                        if 'Text' in will_be_converted_items:
1259
                            for record in blk_tbl_record.iter('AcDbText'):
1260
                                if record.attrib['Layer'] not in exclude_layers:
1261
                                    node = self.text_to_xml(record)
1262
                                    if node:
1263
                                        textInfo.append(node)
1264

    
1265
                            #for record in blk_tbl_record.iter('AcDbAttribute'):
1266
                            #    if record.attrib['Layer'] not in exclude_layers:
1267
                            #        node = self.text_to_xml(record)
1268
                            #        if node:
1269
                            #            textInfo.append(node)    
1270

    
1271
                        for blk_ref in blk_tbl_record.iter('AcDbBlockReference'):
1272
                            if 'Symbol' in will_be_converted_items:
1273
                                if blk_ref.attrib['Layer'] not in exclude_layers:
1274
                                    node = None
1275
                                    block_name = blk_ref.attrib['Name']
1276
                                    if block_name.split('+')[0] == 'Graphic':
1277
                                        node = self.graphic_to_xml(blk_ref)
1278
                                        if node:
1279
                                            graphics_node.append(node)
1280
                                    else:
1281
                                        node = self.symbol_to_xml(blk_ref, mapping_configs)
1282
                                        if node:
1283
                                            symbols_node.append(node)
1284

    
1285
                            if 'Text' in will_be_converted_items:
1286
                                if blk_ref.attrib['Layer'] not in exclude_layers:
1287
                                    #angle = round(float(blk_ref.attrib['Angle']), 2)
1288
                                    for record in blk_ref.iter('AcDbAttribute'):
1289
                                        node = self.text_to_xml(record)
1290
                                        if node:
1291
                                            textInfo.append(node)
1292

    
1293
                        if 'Line' in will_be_converted_items:
1294
                            for record in blk_tbl_record.iter('AcDbLine'):
1295
                                if record.attrib['Layer'] not in exclude_layers:
1296
                                    nodes = self.lines_to_xml(layers, line_types, record)
1297
                                    if nodes:
1298
                                        for node in nodes:
1299
                                            line_infos.append(node)
1300

    
1301
                            for record in blk_tbl_record.iter('AcDbArc'):
1302
                                if record.attrib['Layer'] not in exclude_layers:
1303
                                    nodes = self.lines_to_xml(layers, line_types, record)
1304
                                    if nodes:
1305
                                        for node in nodes:
1306
                                            line_infos.append(node)
1307

    
1308
                            for record in blk_tbl_record.iter('AcDbPolyline'):
1309
                                if record.attrib['Layer'] not in exclude_layers:
1310
                                    nodes = self.lines_to_xml(layers, line_types, record)
1311
                                    if nodes:
1312
                                        for node in nodes:
1313
                                            line_infos.append(node)
1314

    
1315
                    configs = app_doc_data.getConfigs('Text Style', 'Font Size')
1316
                    fontSize = int(configs[0].value) if configs else -1
1317
                    if fontSize != -1:
1318
                        size = {}
1319
                        for text in textInfo:
1320
                            angle = float(text.find('ANGLE').text) if text.find('ANGLE') is not None else 0
1321
                            width = float(text.find('WIDTH').text) if text.find('WIDTH') is not None else 0
1322
                            height = float(text.find('HEIGHT').text) if text.find('HEIGHT') is not None else 0
1323

    
1324
                            if angle == 0:
1325
                                if height in size:
1326
                                    size[height] += 1
1327
                                else:
1328
                                    size[height] = 1
1329
                            else:
1330
                                if width in size:
1331
                                    size[width] += 1
1332
                                else:
1333
                                    size[width] = 1
1334
                        
1335
                        targetSize, maxCount = 10, 0
1336
                        for key, value in size.items():
1337
                            if maxCount < value:
1338
                                targetSize = key
1339

    
1340
                        for text in textInfo:
1341
                            _text = text.find('VALUE').text
1342
                            angle = float(text.find('ANGLE').text) if text.find('ANGLE') is not None else 0
1343
                            width = float(text.find('WIDTH').text) if text.find('WIDTH') is not None else 0
1344
                            height = float(text.find('HEIGHT').text) if text.find('HEIGHT') is not None else 0
1345

    
1346
                            multiple = 1 if '\n' not in _text else _text.count('\n') + 1
1347
                            if angle == 0:
1348
                                text.find('HEIGHT').text = str(targetSize * multiple)
1349
                            else:
1350
                                text.find('WIDTH').text = str(targetSize * multiple)
1351

    
1352
                    id2_xml.write(id2_xml_path, encoding="utf-8", xml_declaration=True)
1353
                    """up to here"""
1354

    
1355
                except Exception as ex:
1356
                    from App import App 
1357
                    from AppDocData import MessageType
1358

    
1359
                    message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1360
                              f"{sys.exc_info()[-1].tb_lineno}"
1361
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1362
            else:
1363
                from App import App
1364
                from AppDocData import MessageType
1365

    
1366
                message = 'There is no xml file'
1367
                App.mainWnd().addMessage.emit(MessageType.Error, message)
1368

    
1369
            count += 1
1370
            mainWnd.progress_bar.setValue(count)
1371
            QApplication.processEvents()
1372

    
1373
        mainWnd.progress_bar.setValue(mainWnd.progress_bar.maximum())
1374

    
1375
        QMessageBox.information(self, self.tr('Information'), self.tr('Importing finished!'),
1376
                                QMessageBox.Close)
1377

    
1378
    def symbol_info(self, blk_ref_node):
1379
        """try to convert block reference element to id2 xml"""
1380
        import symbol
1381

    
1382
        try:
1383
            symbolTypes = [symbolType[2] for symbolType in AppDocData.instance().getSymbolTypeList()]
1384
            _name = blk_ref_node.attrib['Name']
1385
            if '+' in _name and _name.split('+')[0] in symbolTypes:
1386
                name = _name.split('+')[1]
1387
                type = _name.split('+')[0]
1388
            elif '+' in _name:
1389
                name = _name.split('+')[1]
1390
                type = 'Valves' if _name.split('+')[0] != 'Equipment' else 'Vessels'
1391
            else:
1392
                name = _name
1393
                type = 'Valves'
1394
            _origin = self.convert_to_image_coords([float(blk_ref_node.attrib['X']), float(blk_ref_node.attrib['Y'])])
1395

    
1396
            """check if maxtents or minextents attribute exists"""
1397
            if 'MaxExtents' not in blk_ref_node.attrib or 'MinExtents' not in blk_ref_node.attrib:
1398
                return None
1399

    
1400
            min_extents, max_extents = None, None
1401
            tokens = blk_ref_node.attrib['MaxExtents'].strip('()').split(',')
1402
            if 3 == len(tokens):
1403
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1404

    
1405
            tokens = blk_ref_node.attrib['MinExtents'].strip('()').split(',')
1406
            if 3 == len(tokens):
1407
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1408

    
1409
            _height = math.ceil(abs(max_extents[1] - min_extents[1]))
1410
            _width = math.ceil(abs(max_extents[0] - min_extents[0]))
1411

    
1412
            loc = [min(min_extents[0], max_extents[0]), min(min_extents[1], max_extents[1])]
1413

    
1414
            origin = [math.ceil(_origin[0] - loc[0]), math.ceil(_origin[1] - loc[1])]
1415
            origin[0] = 0 if origin[0] <= 0 else (_width if origin[0] > _width else origin[0])
1416
            origin[1] = 0 if origin[1] <= 0 else (_height if origin[1] > _height else origin[1])
1417

    
1418
            points = []
1419
            if blk_ref_node.attrib['Nodes']:
1420
                _points = blk_ref_node.attrib['Nodes'].replace('(', '').replace(')', '').split('/')
1421
                if _points:
1422
                    for _point in _points:
1423
                        _point = _point.split(',')
1424
                        point = self.convert_to_image_coords([float(_point[0]), float(_point[1])])
1425
                        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]))
1426
                        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]))
1427
                        points.append(point)
1428

    
1429
            strPoints = []
1430
            for point in points:
1431
                strPoint = 'AUTO,' + str(point[0]) + ',' + str(point[1]) + ',0,None,X,Secondary'
1432
                strPoints.append(strPoint)
1433

    
1434
            ret = symbol.SymbolBase(name, type, 0.75, 0, 1, 0, 0, 0, \
1435
                                    "{},{}".format(origin[0], origin[1]), \
1436
                                    '/'.join(strPoints), \
1437
                                    'None', '', 0, 0, -1, \
1438
                                    iType=-2, detectFlip=0, text_area='')
1439

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

    
1444
            return [_name, ret, loc, _width, _height]
1445
        except Exception as ex:
1446
            from App import App
1447
            from AppDocData import MessageType
1448

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

    
1453
        return None
1454

    
1455
    def convert_to_image_coords(self, pt):
1456
        """convert autocad coordinates to image coordinates"""
1457
        if self.ui.checkBoxAuto.isChecked():
1458
            return [pt[0] * self.scales[0] + self.offsets[0], (self.offsets[2] - pt[1]) * self.scales[1] + self.offsets[1]]
1459
        else:
1460
            return [pt[0] * self.scales[0] + self.offsets[0], self.offsets[2] - pt[1] * self.scales[1] + self.offsets[1]]
1461

    
1462
    def graphic_to_xml(self, blk_ref_node):
1463
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1464

    
1465
        try:
1466
            node = Element('GRAPHIC')
1467
            uidNode = Element('UID')
1468
            uidNode.text = str(uuid.uuid4())
1469
            node.append(uidNode)
1470

    
1471
            nameNode = Element('NAME')
1472
            nameNode.text = blk_ref_node.attrib['Name']
1473
            node.append(nameNode)
1474

    
1475
            min_extents, max_extents = None, None
1476
            tokens = blk_ref_node.attrib['MaxExtents'].strip('()').split(',')
1477
            if 3 == len(tokens):
1478
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1479

    
1480
            tokens = blk_ref_node.attrib['MinExtents'].strip('()').split(',')
1481
            if 3 == len(tokens):
1482
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1483

    
1484
            locNode1 = Element('LOCATION1')
1485
            locNode1.text = '{},{}'.format(min_extents[0], min_extents[1])
1486
            node.append(locNode1)
1487

    
1488
            locNode2 = Element('LOCATION2')
1489
            locNode2.text = '{},{}'.format(max_extents[0], max_extents[1])
1490
            node.append(locNode2)
1491

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

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

    
1500
            return None
1501

    
1502
        return node
1503

    
1504
    def symbol_to_xml(self, blk_ref_node, mapping_configs: list) -> str:
1505
        """try to convert block reference element to id2 xml"""
1506
        from SymbolSvgItem import SymbolSvgItem
1507

    
1508
        try:
1509
            name = blk_ref_node.attrib['Name']
1510
            origin = self.convert_to_image_coords([float(blk_ref_node.attrib['X']), float(blk_ref_node.attrib['Y'])])
1511
            flip = float(blk_ref_node.attrib['ScaleFactors'].replace('(', '').replace(')', '').split(',')[0])
1512
            flip = 1 if flip < 0 else 0
1513
            if flip == 0:
1514
                angle = round(2 * math.pi - float(blk_ref_node.attrib['Angle']), 2)
1515
            else:
1516
                angle = round(float(blk_ref_node.attrib['Angle']), 2)
1517

    
1518
            if angle > 6.28:
1519
                angle = angle - 2 * math.pi
1520
            elif angle == 6.28:
1521
                angle = 0.0
1522

    
1523
            """check if maxtents or minextents attribute exists"""
1524
            if 'MaxExtents' not in blk_ref_node.attrib or 'MinExtents' not in blk_ref_node.attrib:
1525
                return None
1526

    
1527
            min_extents, max_extents = None, None
1528
            tokens = blk_ref_node.attrib['MaxExtents'].strip('()').split(',')
1529
            if 3 == len(tokens):
1530
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1531

    
1532
            tokens = blk_ref_node.attrib['MinExtents'].strip('()').split(',')
1533
            if 3 == len(tokens):
1534
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1535

    
1536
            _height = abs(max_extents[1] - min_extents[1])
1537
            _width = abs(max_extents[0] - min_extents[0])
1538

    
1539
            '''
1540
            allowed_error = 0.01
1541
            if abs(angle - 1.57) < allowed_error:
1542
                _height, _width = _width, _height
1543
                #origin[0], origin[1] = origin[0] - _width, origin[1] - _height + _width
1544
            elif abs(angle - 4.71) < allowed_error:
1545
                _height, _width = _width, _height
1546
                #origin[0], origin[1] = origin[0] - _width, origin[1] + _height - _width
1547
            '''
1548

    
1549
            uid = None
1550
            for config in mapping_configs:
1551
                if name in config.value.split(','):
1552
                    uid = config.key
1553
                    break
1554
            if uid:
1555
                app_doc_data = AppDocData.instance()
1556
                symbol = app_doc_data.getSymbolByQuery('UID', uid)
1557
                svg_file_path = os.path.join(app_doc_data.getCurrentProject().getSvgFilePath(), symbol.getType(),
1558
                                             symbol.getName() + '.svg')
1559

    
1560
                item = SymbolSvgItem.createItem(symbol.getType(), symbol.getName(), path=svg_file_path)
1561
                loc = [min(min_extents[0], max_extents[0]), min(min_extents[1], max_extents[1])]
1562
                item.buildItem(name, symbol.getType(), angle, loc, (_width, _height), origin,
1563
                               connPts=symbol.parse_connection_pts(origin), parentSymbol=None, childSymbol=None,
1564
                               hasInstrumentLabel=False, dbUid=uid)
1565
                item.converted = True
1566
                item.flip = flip
1567

    
1568
                app_doc_data = AppDocData.instance()
1569
                for area in app_doc_data.getAreaList():
1570
                    if area.contains([origin[0], origin[1]]):
1571
                        item.area = area.name
1572
                        break
1573

    
1574
                return item.toXml()
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 text_to_xml(self, text_node):
1586
        """
1587
        try to convert text element to id2 xml
1588
        @param text_node:
1589
        @param id2_bbox:
1590
        @param autocad_bbox:
1591
        @return:
1592
        """
1593
        import math
1594
        from EngineeringTextItem import QEngineeringTextItem
1595

    
1596
        try:
1597
            if text_node.get('MinExtents') == None or text_node.get('MaxExtents') == None:
1598
                return None
1599
            
1600
            loc = self.convert_to_image_coords([float(text_node.attrib['X']), float(text_node.attrib['Y'])])
1601

    
1602
            text = text_node.text.strip()
1603
            if not text.replace(' ', '').replace('\n', ''):
1604
                return
1605
            #if text == '-':
1606
            #    print('a')
1607
            angle = round(float(text_node.attrib['Angle']), 2)
1608
            if 'IsMirroredInX' in text_node.attrib and text_node.attrib['IsMirroredInX'] == 'True':
1609
                if abs(math.pi * 0.5 - angle) < 0.01 or abs(math.pi * 1.5 - angle) < 0.01:
1610
                    angle += math.pi
1611
                    angle = angle - math.pi * 2 if angle > math.pi * 2 else angle
1612

    
1613
            if 'IsMirroredInY' in text_node.attrib and text_node.attrib['IsMirroredInY'] == 'True':
1614
                if abs(angle) < 0.01 or abs(math.pi - angle) < 0.01:
1615
                    angle += math.pi
1616
                    angle = angle - math.pi * 2 if angle > math.pi * 2 else angle
1617

    
1618
            min_extents, max_extents = None, None
1619
            tokens = text_node.attrib['MaxExtents'].strip('()').split(',')
1620
            if 3 == len(tokens):
1621
                max_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1622

    
1623
            tokens = text_node.attrib['MinExtents'].strip('()').split(',')
1624
            if 3 == len(tokens):
1625
                min_extents = self.convert_to_image_coords([float(tokens[0]), float(tokens[1])])
1626

    
1627
            _height = math.ceil(abs(max_extents[1] - min_extents[1]))
1628
            if _height < 10:
1629
                _height = 10
1630
            _width = math.ceil(abs(max_extents[0] - min_extents[0]))
1631
            if _width < 10:
1632
                _width = 10
1633

    
1634
            allowed_error = 0.01
1635
            if abs(angle - 1.57) < allowed_error:
1636
            #    height, _width = _width, _height
1637
            #    loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
1638
                loc[0] -= _width - 4
1639
                loc[1] -= 4
1640
            elif abs(angle - 4.71) < allowed_error:
1641
            #    _height, _width = _width, _height
1642
            #    loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
1643
                loc[0] -= _width - 4
1644
                loc[1] -= 4
1645

    
1646
            #_height = round(float(text_node.attrib['Height']) * self.scales[1])
1647
            #_width = round(_height * len(text) * self.text_scale)
1648
            ##_width = round(float(text_node.attrib['Width']))
1649
            loc[1] -= _height - 4
1650

    
1651
            item = QEngineeringTextItem()
1652
            item.setPlainText(text)
1653
            item.loc = loc
1654
            item.size = (_width, _height)
1655
            item.angle = angle
1656

    
1657
            for area in self.areas:
1658
                if area.contains([loc[0], loc[1]]):
1659
                    item.area = area.name
1660
                    break
1661

    
1662
            node = item.toXml()
1663

    
1664
            for area in self.typicals:
1665
                if area.contains([loc[0], loc[1]]):
1666
                    node = None
1667

    
1668
            return node
1669
        except Exception as ex:
1670
            from App import App
1671
            from AppDocData import MessageType
1672

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

    
1677
    def lines_to_xml(self, layers: list, line_types: list, line_node) -> list:
1678
        """try to convert line element to id2 xml"""
1679
        from LineTypeConditions import LineTypeConditions
1680
        from EngineeringLineItem import QEngineeringLineItem
1681

    
1682
        def get_line_type(layers: list, line_types: list, layer_name: str, line_type: str) -> str:
1683
            """return line type"""
1684

    
1685
            '''
1686
            res = line_type
1687
            if line_type.upper() == 'ByLayer'.upper():
1688
                matches = [layer for layer in layers if layer[0] == layer_name]
1689
                if matches:
1690
                    line_type_oid = matches[0][1]
1691
                    matches = [line_node for line_node in line_types if line_node[0] == line_type_oid]
1692
                    if matches:
1693
                        res = matches[0][1]
1694
            '''
1695
            res = layer_name
1696

    
1697
            return res
1698

    
1699
        try:
1700
            pts = []
1701
            for vertex in line_node.iter('Vertex'):
1702
                pts.append(self.convert_to_image_coords([float(vertex.attrib['X']), float(vertex.attrib['Y'])]))
1703

    
1704
            # append first point if 'Closed' attribute is True
1705
            if 'Closed' in line_node.attrib and line_node.attrib['Closed'].upper() == 'True'.upper():
1706
                pts.append(pts[0])
1707

    
1708
            """get id2 line type uid"""
1709
            line_type, line_type_cond = get_line_type(layers, line_types, line_node.attrib['Layer'],
1710
                                                      line_node.attrib['Linetype']), None
1711
            model = self.ui.treeViewLineType.model()
1712
            for row in range(model.rowCount()):
1713
                parent_index = model.index(row, 0)
1714
                id2_line_type = model.itemFromIndex(parent_index).text()
1715

    
1716
                child_count = model.rowCount(parent_index)
1717
                for child_row in range(child_count):
1718
                    child_index = model.index(child_row, 1, parent_index)
1719
                    autocad_line_type = model.itemFromIndex(child_index).text()
1720
                    if autocad_line_type == line_type:
1721
                        matches = [item for item in LineTypeConditions.items() if item.name == id2_line_type]
1722
                        if matches:
1723
                            line_type_cond = matches[0]
1724
                        break
1725
            """up to here"""
1726

    
1727
            nodes = []
1728
            for idx in range(len(pts) - 1):
1729
                start, end = pts[idx], pts[idx + 1]
1730
                dx, dy = end[0] - start[0], end[1] - start[1]
1731

    
1732
                """check if length is zero"""
1733
                length = dx*dx + dy*dy
1734
                if length == 0:
1735
                    continue
1736

    
1737
                #if 1 < length < 30:
1738
                #    a = 1
1739

    
1740
                item = QEngineeringLineItem(vertices=[start, end], thickness=5)
1741

    
1742
                # import lines which is horizontal or vertical
1743
                if self.ui.checkBoxDiagonal.isChecked():
1744
                    angle = math.degrees(item.angle())
1745
                    if not ((-5 < angle < 5) or (85 < angle < 95) or (175 < angle < 185) or (355 < angle < 365)):
1746
                        continue
1747

    
1748
                item.area = 'Drawing'
1749

    
1750
                if line_type_cond:
1751
                    item.lineType = line_type_cond.name
1752
                    node = item.toXml()
1753
                    nodes.append(node)
1754

    
1755
            return nodes
1756
        except Exception as ex:
1757
            from App import App
1758
            from AppDocData import MessageType
1759

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

    
1764
        return None
1765

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