프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / LineNoTracer.py @ dc379815

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

1
# coding: utf-8
2
""" This is line no tracer module """
3

    
4
import sys
5
import math
6
import asyncio
7
import shapely
8
from AppDocData import AppDocData, MessageType
9
from EngineeringLineItem import QEngineeringLineItem
10
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
11
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
12
from SymbolSvgItem import SymbolSvgItem
13
from EngineeringTextItem import QEngineeringTextItem
14
from EngineeringUnknownItem import QEngineeringUnknownItem
15

    
16
try:
17
    from PyQt5.QtCore import *
18
    from PyQt5.QtGui import *
19
    from PyQt5.QtWidgets import *
20
except ImportError:
21
    try:
22
        from PyQt4.QtCore import *
23
        from PyQt4.QtGui import *
24
    except ImportError:
25
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
26

    
27

    
28
class LineNoTracer:
29
    '''
30
        @history    2018.04.26 Jeongwoo Variable name changed (texts → lineNos)
31
    '''
32

    
33
    def __init__(self, symbols, lines, lineNos, specBreaks, lineIndicators, vendors, end_breaks):
34
        try:
35
            self._symbols = symbols
36
            self._lines = lines
37
            self._lineNos = lineNos
38
            self._spec_breaks = specBreaks
39
            self._lineIndicator = lineIndicators
40
            self._end_breaks = end_breaks
41
            self.maxValue = None
42

    
43
            """
44
            for spec in self._specBreak:
45
                for attr in spec.attrs:
46
                    if type(attr) is tuple and attr[1] != '':
47
                        self._specBreakUID.append(attr[1])
48
            """
49
        except Exception as ex:
50
            from App import App
51

    
52
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
53
                                                           sys.exc_info()[-1].tb_lineno)
54
            App.mainWnd().addMessage.emit(MessageType.Error, message)
55

    
56
    '''
57
        @brief  find primary lines connected to given line no
58
        @author humkyung
59
    '''
60

    
61
    def find_primary_lines(self, lineno, include_signal=True):
62
        from EngineeringLineItem import QEngineeringLineItem
63
        from EngineeringRunItem import QEngineeringRunItem
64

    
65
        connected_items = []
66

    
67
        _from = lineno.prop('From')
68
        _to = lineno.prop('To')
69
        if _from and _to and lineno.empty():
70
            connected_items = self.find_connected_objects(_from, to=_to, primary=True, include_signal=include_signal)
71
            if _from in connected_items and _to in connected_items:
72
                start = connected_items.index(_from)
73
                end = connected_items.index(_to)
74
                if start < end:
75
                    connected_items = connected_items[start:end + 1]
76
                else:
77
                    connected_items = connected_items[end:start + 1]
78
                    connected_items.reverse()
79
        elif (not _from or not _to) and (1 == len(lineno.conns)):
80
            connected_items = self.find_connected_objects(lineno.conns[0], include_signal=include_signal)
81

    
82
        # print(connected_items)
83
        if connected_items:
84
            for item in connected_items:
85
                item.owner = lineno  # set item's owner
86

    
87
            line_run = QEngineeringRunItem()
88
            line_run.items = connected_items
89
            line_run.arrange_flow_direction()
90
            line_run.owner = lineno
91
            lineno.runs.append(line_run)
92

    
93
            lineno.set_property('From', connected_items[0])
94
            lineno.set_property('To', connected_items[-1])
95

    
96
        if _to is not None and connected_items and connected_items[-1] is not _to:
97
            _to.owner = None
98

    
99
        return connected_items
100

    
101
    '''
102
        @brief  find secondary lines
103
        @author humkyung
104
    '''
105

    
106
    def find_secondary_lines(self, lines_and_symbols, include_signal=True, is_trim=False):
107
        from EngineeringAbstractItem import QEngineeringAbstractItem
108
        from EngineeringLineItem import QEngineeringLineItem
109
        from EngineeringRunItem import QEngineeringRunItem
110
        from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
111

    
112
        try:
113
            foundCount = 1
114
            while foundCount:
115
                foundCount = 0
116
                notMatches = []
117
                for line in lines_and_symbols:
118
                    if line.owner is not None: continue
119

    
120
                    if not include_signal and type(line) is QEngineeringLineItem and not line.is_piping(): continue
121

    
122
                    if not is_trim:
123
                        line_matches = [x for x in self._lines if
124
                                        x.owner and line.is_connected(x, QEngineeringAbstractItem.CONNECTED_AT_BODY)]
125
                        symbol_matches = [x for x in self._symbols if
126
                                          x.owner and line.is_connected(x) and x.canBeSecondary(line)]
127
                    else:
128
                        line_matches = [x for x in self._lines if x.owner and type(
129
                            x.owner) is QEngineeringTrimLineNoTextItem and line.is_connected(x,
130
                                                                                             QEngineeringAbstractItem.CONNECTED_AT_BODY)]
131
                        symbol_matches = [x for x in self._symbols if x.owner and type(
132
                            x.owner) is QEngineeringTrimLineNoTextItem and line.is_connected(x) and x.canBeSecondary(
133
                            line)]
134

    
135
                    if line_matches or symbol_matches:
136
                        foundCount += 1
137
                        connected_items = self.find_connected_objects(line, include_signal=include_signal)
138

    
139
                        owner = line_matches[0].owner if line_matches else symbol_matches[0].owner
140
                        for item in connected_items:
141
                            item.owner = owner  # set item's owner
142

    
143
                        if connected_items:
144
                            line_run = QEngineeringRunItem()
145
                            line_run.items = connected_items
146
                            line_run.arrange_flow_direction()
147
                            if line_run.items is not None and len(line_run.items) > 0:
148
                                line_run.owner = owner
149
                                owner.runs.append(line_run)
150
                    else:
151
                        notMatches.append(line)
152
                # lines_and_symbols = notMatches
153
        except Exception as ex:
154
            from App import App
155

    
156
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
157
                                                           sys.exc_info()[-1].tb_lineno)
158
            App.mainWnd().addMessage.emit(MessageType.Error, message)
159

    
160
    '''
161
        @brief      trace line no
162
        @author     humkyung
163
        @date       2018.04.16
164
        @history    2018.04.26 Jeongwoo docDatalineNos = self.lineNos, Not For loop
165
                    humkyung 2018.05.08 add flow arrow
166
                    Jeongwoo 2018.05.14 Add [runs] on Primary/Secondary Line - Need to modify Secondary Line
167
                    Jeongwoo 2018.05.15 Make Comments [lineno.conns[0].owner = lineno]
168
                    Jeongwoo 2018.05.17 Modify find secondary lines with 'while'
169
                                        Modify find secondary lines with 'while' used sublist for unconnected line
170
                    humkyung 2018.05.18 set start line's owner before tracing
171
    '''
172

    
173
    def execute(self, displayMessage, updateProgress, toler=600):
174
        from EngineeringLineItem import QEngineeringLineItem
175
        from SymbolSvgItem import SymbolSvgItem
176
        from EngineeringRunItem import QEngineeringRunItem
177
        from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
178
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
179

    
180
        try:
181
            app_doc_data = AppDocData.instance()
182

    
183
            configs = app_doc_data.getConfigs('Line No', 'Delimiter')
184
            if 1 == len(configs):
185
                #configs = app_doc_data.getConfigs('Line No', 'Configuration')
186

    
187
                for line_no in self._lineNos:
188
                    _from = line_no.prop('From')
189
                    _to = line_no.prop('To')
190

    
191
                    line_no.conns.clear()
192
                    minDist = None
193
                    startLine = None
194
                    for line in [line for line in self._lines if line.owner is None and line.is_piping(strong=True)]:
195
                        dist = line.distanceTo((line_no.center().x(), line_no.center().y()))
196
                        if (minDist is None) or (dist < minDist):
197
                            minDist = dist
198
                            startLine = line
199
                    if (startLine is not None) and (minDist < toler):
200
                        line_no.conns.append(startLine)
201
                        if not _from or not _to:
202
                            startLine.owner = line_no
203

    
204
                    if _from and _to and \
205
                            (type(_from) is QEngineeringLineItem or issubclass(type(_from), SymbolSvgItem)) and \
206
                            (type(_to) is QEngineeringLineItem or issubclass(type(_to), SymbolSvgItem)):
207
                        _from.owner = line_no
208
                        _to.owner = line_no
209
                        line_no._fixed = True
210
                    else:
211
                        line_no.set_property('From', None)
212
                        line_no.set_property('To', None)
213
                        line_no._fixed = False
214

    
215
                self.maxValue = len(self._lineNos) + 1  # line no's count + secondary line
216

    
217
                # find primary lines
218
                # sort line no with from,to value
219
                self._lineNos.sort(
220
                    key=lambda line_no: (1 if line_no.prop('From') else 0) + (1 if line_no.prop('To') else 0),
221
                    reverse=True)
222
                for lineno in self._lineNos:
223
                    if displayMessage: displayMessage.emit('{} {}'.format(lineno.text(), 'Topology Construction'))
224
                    self.find_primary_lines(lineno, include_signal=False)
225
                    if updateProgress: updateProgress.emit(self.maxValue)
226

    
227
                # find secondary lines
228
                lines_and_symbols = self._lines + self._symbols
229
                self.find_secondary_lines(lines_and_symbols, include_signal=False)
230

    
231
                # double check conn line cuz startLine may need at first step
232
                for lineno in self._lineNos:
233
                    lineno.conns.clear()
234
                    minDist = None
235
                    startLine = None
236
                    if len(lineno.runs) is 0:
237
                        continue
238
                    for line in [line for line in lineno.runs[0].items if
239
                                 type(line) is QEngineeringLineItem and line.is_piping(strong=True)]:
240
                        dist = line.distanceTo((lineno.center().x(), lineno.center().y()))
241
                        if (minDist is None) or (dist < minDist):
242
                            minDist = dist
243
                            startLine = line
244
                    if (startLine is not None):  # and (minDist < toler):
245
                        lineno.conns.append(startLine)
246

    
247
                if updateProgress: updateProgress.emit(self.maxValue)
248

    
249
            # make trim lines
250
            updateProgress.emit(-1)  # reset progressbar
251
            displayMessage.emit('Unknown line Topology Construction')
252
            orphanLines = [line for line in self._lines if line.owner is None]
253
            orphanSymbols = [symbol for symbol in self._symbols if
254
                             symbol.owner is None and type(symbol) is not QEngineeringEquipmentItem]
255
            if orphanLines + orphanSymbols:
256
                self.maxValue = len(orphanLines) + 1
257
                orphanLines = sorted(orphanLines, key=lambda param: param.length(), reverse=True)
258
                orphans = orphanLines + orphanSymbols
259
                while len(orphans) > 0:
260
                    trimLineNo = QEngineeringTrimLineNoTextItem()
261
                    trimLineNo.conns.append(orphans[0])
262
                    orphans[0].owner = trimLineNo
263
                    # orphanLines[0].linkedItem = trimLineNo
264

    
265
                    connectedItems = self.find_primary_lines(trimLineNo)
266
                    for item in connectedItems:
267
                        if item in orphans:
268
                            orphans.remove(item)
269
                            updateProgress.emit(self.maxValue)
270

    
271
                    self.find_secondary_lines(orphans, is_trim=True)
272
                    for item_index in reversed(range(len(orphans))):
273
                        item = orphans[item_index]
274
                        if item.owner is not None:
275
                            orphans.remove(item)
276
                            updateProgress.emit(self.maxValue)
277

    
278
                    app_doc_data.tracerLineNos.append(trimLineNo)
279

    
280
            if updateProgress: updateProgress.emit(self.maxValue)
281
        except Exception as ex:
282
            from App import App
283

    
284
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
285
                                                           sys.exc_info()[-1].tb_lineno)
286
            App.mainWnd().addMessage.emit(MessageType.Error, message)
287

    
288
    '''
289
        @brief      find objects connected to given line while loop
290
        @author     humkyung
291
        @date       2018.04.16
292
        @history    humkyung 2018.05.08 find symbol or line connected to given object
293
                    humkyung 2018.05.10 set found object's owner
294
                    humkyung 2018.05.17 try to connect both symbol and line
295
                    humkyung 2018.06.22 order connected objects
296
    '''
297

    
298
    def find_connected_objects(self, start, to=None, primary=False, include_signal=True):
299
        from EngineeringLineItem import QEngineeringLineItem
300
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
301
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
302
        from SymbolSvgItem import SymbolSvgItem
303

    
304
        visited = [start]
305
        break_at_first = None
306

    
307
        try:
308
            pool = []
309
            pool.append((0, start))
310

    
311
            while len(pool) > 0:
312
                sign, obj = pool.pop()
313

    
314
                # """ check obj is upstream or downstream of spec break """
315
                # matches = [spec_break for spec_break in self._spec_breaks if spec_break.is_connected(obj)]
316
                # if matches or issubclass(type(obj), QEngineeringEquipmentItem):
317
                #    visited.pop(visited.index(obj))
318
                #    continue
319
                # print('obj={} pool={}'.format(obj, pool))
320

    
321
                match = False
322
                if not primary:
323
                    for end_break in self._end_breaks:
324
                        if obj is end_break.owner or obj is end_break.prop('Connected Item'):
325
                            if end_break.owner.is_connected(end_break.prop('Connected Item')) and \
326
                                    end_break.prop('Connected Item').is_connected(end_break.owner) and \
327
                                    len(visited) is not 1:
328
                                match = True
329
                                if obj.is_connected(break_at_first):
330
                                    visited.pop(visited.index(obj))
331
                                break
332
                            elif end_break.owner.is_connected(end_break.prop('Connected Item')) and \
333
                                    end_break.prop('Connected Item').is_connected(end_break.owner):
334
                                break_at_first = obj
335
                                break
336

    
337
                if issubclass(type(obj), QEngineeringEquipmentItem):
338
                    visited.pop(visited.index(obj))
339
                    continue
340
                elif match:
341
                    continue
342

    
343
                """ end loop if obj is to """
344
                if to is not None and str(obj.uid) == str(to.uid): break
345

    
346
                # nextmatches list always has one item
347
                if type(obj) is QEngineeringLineItem:
348
                    symbolMatches = [x for x in self._symbols if (x.owner is None or x.owner == start.owner) and (
349
                            x not in visited) and obj.is_connected(x)]
350
                    if include_signal:
351
                        lineMatches = [x for x in self._lines if
352
                                       (x.owner is None or x.owner == start.owner) and (x is not obj) and (
353
                                               x not in visited) and obj.is_connected(x)]
354
                    else:
355
                        lineMatches = [x for x in self._lines if
356
                                       x.is_piping() and (x.owner is None or x.owner == start.owner) and (
357
                                               x is not obj) and (x not in visited) and obj.is_connected(x)]
358
                    nextMatches = symbolMatches + lineMatches
359

    
360
                elif issubclass(type(obj), SymbolSvgItem):
361
                    # symbol can be connected with line and another symbol at the same time
362
                    if include_signal:
363
                        lineMatches = [x for x in self._lines if (x.owner is None or x.owner == start.owner) and (
364
                                x not in visited) and obj.is_connected(x)]
365
                    else:
366
                        lineMatches = [x for x in self._lines if
367
                                       x.is_piping() and (x.owner is None or x.owner == start.owner) and (
368
                                               x not in visited) and obj.is_connected(x)]
369
                    symbolMatches = [x for x in self._symbols if
370
                                     (x.owner is None or x.owner == start.owner) and (x is not obj) and (
371
                                             x not in visited) and obj.is_connected(x, None)]
372
                    nextMatches = symbolMatches + lineMatches
373

    
374
                    if len(nextMatches) > 1:  # choose one if connected items are more than 2
375
                        matches = [x for x in visited if obj.is_connected(x)]
376
                        if matches:
377
                            next_connected = [x for x in nextMatches if obj.next_connected(x, matches[0])]
378

    
379
                            if next_connected:
380
                                nextMatches = next_connected
381
                            else:
382
                                nextMatches = []
383

    
384
                    # if obj symbol has break connector and nextMatch connected that connector than break line group
385
                    if nextMatches and obj.break_connector and [index for index in obj.break_connector if
386
                                                                obj.connectors[index].connectedItem is nextMatches[0]]:
387
                        match = True
388
                        break
389

    
390
                # if obj item connected symbol that has break connector then break line group
391
                if nextMatches:
392
                    pop_index = []
393
                    index = 0
394
                    for nextMatch in nextMatches:
395
                        if hasattr(nextMatch, 'break_connector'):
396
                            if [index for index in nextMatch.break_connector if
397
                                nextMatch.connectors[index].connectedItem is obj]:
398
                                pop_index.append(index)
399
                        index += 1
400

    
401
                    for index in reversed(pop_index):
402
                        nextMatches.pop(index)
403

    
404
                        # order connected objects
405
                matches = []
406
                matches.extend(nextMatches)
407

    
408
                if sign == 0 and len(matches) > 1:
409
                    mid = int(len(matches) * 0.5)
410
                    lhs = matches[0:mid]
411
                    rhs = matches[mid:]
412
                elif sign == -1:
413
                    lhs = matches
414
                    rhs = []
415
                else:
416
                    lhs = []
417
                    rhs = matches
418

    
419
                for match in lhs:
420
                    # print(match)
421
                    pool.append((-1, match))
422
                    visited.insert(0, match)
423

    
424
                for match in rhs:
425
                    # print(match)
426
                    pool.append((1, match))
427
                    visited.append(match)
428
                # up to here
429

    
430
        except Exception as ex:
431
            from App import App
432

    
433
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
434
                                                           sys.exc_info()[-1].tb_lineno)
435
            App.mainWnd().addMessage.emit(MessageType.Error, message)
436

    
437
        # print(visited)
438
        return visited
439

    
440

    
441
'''
442
    @brief      connect attributes
443
    @author     humkyung
444
    @date       2018.06.17
445
    @history    humkyung 2018.06.21 paste connect attributes codes from recognizeLine function
446
                kyouho  2018.09.14  clear Item's owner 
447
'''
448
async def connectAttrImpl(worker, update_line_type, update_flow_mark, update_spec):
449
    from App import App
450
    import uuid
451
    from LineNoTracer import LineNoTracer
452
    from AppDocData import AppDocData
453
    from EngineeringAbstractItem import QEngineeringAbstractItem
454
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
455
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
456
    from EngineeringReducerItem import QEngineeringReducerItem
457
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
458
    from QEngineeringOPCItem import QEngineeringOPCItem
459
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
460
    from EngineeringVendorItem import QEngineeringVendorItem
461
    from EngineeringEndBreakItem import QEngineeringEndBreakItem
462
    from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
463
    from EngineeringReservedWordTextItem import QEngineeringReservedWordTextItem
464
    from QEngineeringSizeTextItem import QEngineeringSizeTextItem
465
    from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
466
    from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
467
    from QEngineeringTagNoTextItem import QEngineeringTagNoTextItem
468
    from EngineeringErrorItem import QEngineeringErrorItem
469
    from EngineeringTextItem import QEngineeringTextItem
470
    from SpecialItemTypesDialog import SpecialItemTracer
471

    
472
    try:
473
        docdata = AppDocData.instance()
474
        worker.display_message.emit('Initializing...')
475

    
476
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
477
        for item in ALL_ITEM:
478
            item.setVisible(False)
479
        QApplication.processEvents()
480

    
481
        symbols = []
482
        lines = [item for item in worker.scene.items() if type(item) is QEngineeringLineItem]
483
        lineNos = []
484
        spec_breaks = []
485
        lineIndicator = []
486
        vendor_packages = [item for item in worker.scene.items() if
487
                           type(item) is QEngineeringVendorItem and item.pack_type == 'Vendor Package']
488
        equip_packages = [item for item in worker.scene.items() if
489
                          type(item) is QEngineeringVendorItem and item.pack_type == 'Equipment Package']
490
        end_breaks = []
491
        notes = []
492
        flow_marks = []
493

    
494
        for error_item in [item for item in worker.scene.items() if type(item) is QEngineeringErrorItem]:
495
            error_item.transfer.onRemoved.emit(error_item)
496

    
497
        for end_break in [item for item in worker.scene.items() if type(item) is QEngineeringEndBreakItem]:
498
            if not end_break.prop('Freeze'):
499
                end_break.transfer.onRemoved.emit(end_break)
500
            else:
501
                end_breaks.append(end_break)
502

    
503
        QApplication.processEvents()
504

    
505
        '''
506
        for flow_mark in [item for item in worker.scene.items() if type(item) is QEngineeringFlowMarkItem]:
507
            if not flow_mark.prop('Freeze'):
508
                flow_mark.transfer.onRemoved.emit(flow_mark)
509
            else:
510
                flow_marks.append(flow_mark)
511
        '''
512

    
513
        configs = docdata.getConfigs('Supplied by Tag Rule', 'by Vendor')
514
        vendorTag = configs[0].value if configs else 'By Vendor'
515
        for item in worker.scene.items():
516
            if type(item) is QEngineeringSpecBreakItem:
517
                spec_breaks.append(item)
518
            elif issubclass(type(item), SymbolSvgItem) and not (type(item) is QEngineeringErrorItem) and not (
519
                    type(item) is QEngineeringUnknownItem) and item.type != 'Notes' and not (
520
                    type(item) is QEngineeringEndBreakItem):
521
                matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
522
                if matches:
523
                    item.set_property('Supplied By', vendorTag)
524
                else:
525
                    item.set_property('Supplied By', '')
526
                symbols.append(item)
527
            elif type(item) is QEngineeringLineNoTextItem:
528
                lineNos.append(item)
529
            elif type(item) is QEngineeringLineItem:
530
                # matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
531
                # if not matches: lines.append(item)
532
                # for check line disappear bug
533
                pass
534
                # lines.append(item)
535
            elif type(item) is QEngineeringUnknownItem and item.lineIndicator != 'False':
536
                lineIndicator.append(item)
537
            elif issubclass(type(item), QEngineeringTextItem):
538
                item.owner = None
539
            elif item.type == 'Notes':
540
                notes.append(item)
541

    
542
        QApplication.processEvents()
543

    
544
        # remove unknown line's
545
        pastTrim = docdata.tracerLineNos
546
        treeWidget = App.mainWnd().itemTreeWidget
547
        for pastTrimIndex in reversed(range(len(pastTrim))):
548
            if type(pastTrim[pastTrimIndex]) is QEngineeringTrimLineNoTextItem:
549
                try:
550
                    connected_items = pastTrim[pastTrimIndex].getConnectedItems()
551
                    for item in connected_items:
552
                        treeWidget.addTreeItem(treeWidget.SymbolsTreeItem, item)
553
                    pastTrim[pastTrimIndex].explode()
554
                finally:
555
                    pass
556

    
557
        # trace line no
558
        tracer = LineNoTracer(symbols, lines, lineNos, spec_breaks, lineIndicator, vendor_packages, end_breaks)
559
        tracer.execute(worker.display_message, worker.updateProgress)
560
        # up to here
561

    
562
        # connect attribute
563
        worker.display_message.emit('Connecting Attribute...')
564
        QApplication.processEvents()
565
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
566
        for symbol in symbols:
567
            try:
568
                symbol.connectAttribute(texts)
569
            except Exception as ex:
570
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
571
                                                               sys.exc_info()[-1].tb_lineno)
572
                worker.display_message.emit(message)
573

    
574
        """ try to connect label to valve """
575
        labels = [symbol for symbol in symbols if symbol.iType == 19]  # Labels - Symbol => Instrument
576
        valves = [symbol for symbol in symbols if
577
                  symbol.iType == 34 or symbol.iType == 17 or symbol.iType == 22]  # Specialty Components, In-Line, Relief Devices
578
        for label in labels:
579
            label.connectAttribute(valves, clear=False)
580

    
581
        # for slope, etc.., symbol to line
582
        labels = [symbol for symbol in symbols if symbol.iType == 30]  # Labels - Line => Piping
583
        for label in labels:
584
            label.connectAttribute(lines, clear=False)
585

    
586
        '''
587
        # for alarm, symbol to symbol : no more used
588
        labels = [symbol for symbol in symbols if symbol.iType == 29]  # Labels - Piping Components => Instrument
589
        valves = [symbol for symbol in symbols if symbol.iType == 25]  # System Functions
590
        for label in labels:
591
            label.connectAttribute(valves, clear=False)
592
        '''
593

    
594
        QApplication.processEvents()
595
        for symbol in symbols:
596
            for assoc in symbol.associations():
597
                if assoc.owner is None:
598
                    assoc.owner = symbol
599

    
600
        """ restore note text item owner """
601
        for note in notes:
602
            for noteText in note.associations():
603
                noteText.owner = note
604

    
605
        """ try to find text item's owner """
606
        texts = [item for item in worker.scene.items() if type(item) is QEngineeringReservedWordTextItem]
607
        for text in texts:
608
            text.findOwner(lines)
609

    
610
        QApplication.processEvents()
611
        # restore and save manual edited attr's text item
612
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
613
        targetText = []
614
        for text in texts:
615
            found = False
616
            for symbol in symbols + lines:
617
                for attr in list(symbol.attrs.keys()):
618
                    # skip freezed attr or already consumed by symbol
619
                    if attr.AssocItem == text:
620
                        text.owner = symbol
621
                        found = True
622
                        break
623
                    # restore missing attr
624
                    elif type(attr.AssocItem) is str or type(attr.AssocItem) is uuid.UUID:
625
                        if str(attr.AssocItem) == str(text.uid):
626
                            symbol.add_assoc_item(text)
627
                            attr.AssocItem = text
628
                            text.owner = symbol
629
                            found = True
630
                        break
631
                if found: break
632
            if not found and (
633
                    type(text) is QEngineeringSizeTextItem or type(text) is QEngineeringValveOperCodeTextItem or \
634
                    type(text) is QEngineeringTagNoTextItem or type(text) is QEngineeringTextItem):
635
                targetText.append(text)
636

    
637
        worker.need_update_texts = []
638
        targetText = sorted(targetText, key=lambda param: len(param.text()), reverse=True)
639
        for symbol in symbols + equip_packages:
640
            symbol._skip = False
641
        for text in targetText:
642
            ret = text.findOwner(symbols) if type(text) is not QEngineeringTagNoTextItem else text.findOwner(
643
                symbols + equip_packages)
644
            if ret:
645
                worker.need_update_texts.append([text, ret])
646

    
647
        """ update line type """
648
        if update_line_type:
649
            worker.display_message.emit('Updating Line Type...')
650
            for line in lines:
651
                line.update_line_type()
652

    
653
        QApplication.processEvents()
654
        """ make end break """
655
        make_end_break(worker, end_breaks, lineNos)
656

    
657
        QApplication.processEvents()
658
        """ update spec break """
659
        if update_spec:
660
            make_spec(worker, spec_breaks, lines, lineNos)
661

    
662
        worker.updateProgress.emit(tracer.maxValue)
663

    
664
        # trace special item
665
        worker.display_message.emit('Finding line for special item...')
666
        QApplication.processEvents()
667
        tracer = SpecialItemTracer([item for item in worker.scene.items() if
668
                                    (type(item) is SymbolSvgItem or type(item) is QEngineeringTextItem) and
669
                                    item.special_item_type], lines)
670
        tracer.execute(worker.display_message, worker.updateProgress)
671
        # up to here
672

    
673
        ''' sort run flow order '''
674
        worker.display_message.emit('Sorting Lines...')
675
        QApplication.processEvents()
676
        sort_run_flow(worker)
677

    
678
        QApplication.processEvents()
679
        """ make flow mark """
680
        if update_flow_mark:
681
            make_flow_mark(worker, lines)
682

    
683
        ''' get line no's from/to equipment '''
684
        for lineNo in lineNos:
685
            lineNo.clear_attr_and_assoc_item()
686
            lineNo.EvaluatedEQ()
687

    
688
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
689
        for item in ALL_ITEM:
690
            item.setVisible(True)
691

    
692
    except Exception as ex:
693
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
694
                                                       sys.exc_info()[-1].tb_lineno)
695
        worker.display_message.emit(message)
696
    except:
697
        (type1, value, traceback) = sys.exc_info()
698
        sys.excepthook(type1, value, traceback)
699

    
700

    
701
def make_end_break(worker, end_breaks, lineNos):
702
    ''' make end break '''
703

    
704
    from App import App
705
    import os
706

    
707
    try:
708
        docdata = AppDocData.instance()
709

    
710
        end_break_names = docdata.getSymbolListByType('type', 'End Break')
711
        if len(end_break_names) is not 0:
712
            svgFileName = end_break_names[0].sName
713
            symbol = end_break_names[0]
714
            svgFilePath = os.path.join(docdata.getCurrentProject().getSvgFilePath(), symbol.getType(),
715
                                       svgFileName + '.svg')
716

    
717
            lineNo_froms = []
718
            lineNo_tos = []
719

    
720
            for lineNo in lineNos:
721
                # lineNo_froms.append(lineNo.prop('From')) if lineNo.prop('From') is not None else None
722
                # lineNo_tos.append(lineNo.prop('To')) if lineNo.prop('To') is not None else None
723

    
724
                for run in lineNo.runs:
725
                    start = [run.items[0]]
726
                    end = [run.items[-1]] if run.items[0] is not run.items[-1] else []
727
                    lineNo_froms.extend(start)
728
                    lineNo_tos.extend(end)
729

    
730
            for line_end in lineNo_froms + lineNo_tos:
731
                # print(type(line_end))
732
                for connector in line_end.connectors:
733
                    if connector.connectedItem is not None and type(
734
                            connector.connectedItem.owner) is QEngineeringLineNoTextItem and connector.connectedItem.owner is not line_end.owner:
735
                        end_break = SymbolSvgItem.createItem(symbol.getType(), None, svgFilePath)
736
                        pt = [connector.center()[0] - float(symbol.getOriginalPoint().split(',')[0]),
737
                              connector.center()[1] - float(symbol.getOriginalPoint().split(',')[1])]
738
                        origin = [0, 0]
739
                        if 2 == len(symbol.getOriginalPoint().split(',')):
740
                            tokens = symbol.getOriginalPoint().split(',')
741
                            origin = [pt[0] + float(tokens[0]), pt[1] + float(tokens[1])]
742
                        end_break.buildItem(svgFileName, symbol.getType(), 3.83, pt,
743
                                            [end_break.boundingRect().width(), end_break.boundingRect().height()],
744
                                            origin, [], symbol.getBaseSymbol(), symbol.getAdditionalSymbol(),
745
                                            symbol.getHasInstrumentLabel())
746

    
747
                        end_break.set_property('Connected Item', connector.connectedItem)
748
                        end_break.setToolTip('owner : ' + str(line_end))
749
                        end_break.area = 'Drawing'
750
                        end_break.owner = line_end
751
                        end_breaks.append(end_break)
752

    
753
            if end_breaks:
754
                # check duplication
755
                dupl = set()
756
                for i in range(len(end_breaks)):
757
                    for j in range(len(end_breaks)):
758
                        if i == j:
759
                            continue
760
                        else:
761
                            setI = set([end_breaks[i].owner, end_breaks[i].prop('Connected Item')])
762
                            setJ = set([end_breaks[j].owner, end_breaks[j].prop('Connected Item')])
763
                            if not (setI - setJ):
764
                                index = [i, j]
765
                                index.sort()
766
                                index = tuple(index)
767
                                dupl.add(index)
768
                # print(dupl)
769
                dupl = list(set(
770
                    [(indexSet[1] if not end_breaks[indexSet[1]].prop('Freeze') else indexSet[0]) for indexSet in
771
                     list(dupl)]))
772
                dupl.sort(reverse=True)
773
                # print(dupl)
774
                for index in dupl:
775
                    end_breaks.pop(index)
776

    
777
                for end_break in end_breaks:
778
                    if not end_break.prop('Freeze'):
779
                        end_break.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
780
                        # end break can be modeled only piping line
781
                        if (type(end_break.owner) is not QEngineeringLineItem or (
782
                                type(end_break.owner) is QEngineeringLineItem and (
783
                                end_break.owner.lineType == 'Secondary' or end_break.owner.lineType == 'Primary'))) \
784
                                and (type(end_break.prop('Connected Item')) is not QEngineeringLineItem or (
785
                                type(end_break.prop('Connected Item')) is QEngineeringLineItem and (
786
                                end_break.prop('Connected Item').lineType == 'Secondary' or end_break.prop(
787
                            'Connected Item').lineType == 'Primary'))):
788
                            end_break.addSvgItemToScene(worker.scene)
789

    
790
    except Exception as ex:
791
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
792
                                                       sys.exc_info()[-1].tb_lineno)
793
        worker.display_message.emit(message)
794

    
795

    
796
def make_spec(worker, spec_breaks, lines, lineNos):
797
    ''' update spec break '''
798

    
799
    from App import App
800
    import os, math
801

    
802
    try:
803
        docdata = AppDocData.instance()
804

    
805
        freezed_spec_breaks = []
806
        for spec_break in spec_breaks:
807
            if not spec_break.prop('Freeze'):
808
                spec_break.transfer.onRemoved.emit(spec_break)
809
            else:
810
                freezed_spec_breaks.append(spec_break)
811

    
812
        for line in lines:
813
            line.clear_labels()
814

    
815
        spec_break_names = docdata.getSymbolListByType('type', 'Segment Breaks')
816
        if len(spec_break_names) is not 0:
817
            configs = docdata.getConfigs('Range', 'Detection Ratio')
818
            ratio = float(configs[0].value) if 1 == len(configs) else 1.5
819
            ratio *= 2
820

    
821
            svgFileName = spec_break_names[0].sName
822
            symbol = spec_break_names[0]
823
            svgFilePath = os.path.join(docdata.getCurrentProject().getSvgFilePath(), symbol.getType(),
824
                                       svgFileName + '.svg')
825

    
826
            specBreakAttrsFull = [attr for attr in docdata.getSymbolAttribute('Segment Breaks') if
827
                                  attr.Target == 'ALL' and (
828
                                          attr.AttributeType == 'Spec' or attr.AttributeType == 'String')]
829
            specBreakAttrs = [attr.Attribute for attr in specBreakAttrsFull]
830

    
831
            line_ends = []
832
            # append upstream first and append downstream
833
            for lineNo in lineNos:
834
                for run in lineNo.runs:
835
                    line_ends.append(run.items[0]) if issubclass(type(run.items[0]), SymbolSvgItem) or (
836
                            type(run.items[0]) is QEngineeringLineItem and (
837
                            run.items[0].lineType == 'Secondary' or run.items[
838
                        0].lineType == 'Primary')) else None
839
            for lineNo in lineNos:
840
                for run in lineNo.runs:
841
                    if run.items[0] is not run.items[-1]:
842
                        line_ends.append(run.items[-1]) if issubclass(type(run.items[-1]), SymbolSvgItem) or (
843
                                type(run.items[-1]) is QEngineeringLineItem and (
844
                                run.items[-1].lineType == 'Secondary' or run.items[
845
                            -1].lineType == 'Primary')) else None
846

    
847
            spec_breaks = []
848
            for line_end in line_ends:
849
                for connector in line_end.connectors:
850
                    if connector.connectedItem is not None and type(
851
                            connector.connectedItem.owner) is QEngineeringLineNoTextItem and connector.connectedItem.owner is not line_end.owner:
852
                        spec_break = []  # upstream, downstream, [spec, up value, down value], ... , [ ... ]
853
                        match = False
854
                        for prop, value in [[prop, value] for prop, value in line_end.owner.getAttributes().items()
855
                                            if prop.Attribute in specBreakAttrs]:
856
                            for prop2, value2 in [[prop2, value2] for prop2, value2 in
857
                                                  connector.connectedItem.owner.getAttributes().items() if
858
                                                  prop2.Attribute in specBreakAttrs and prop2.UID == prop.UID]:
859
                                if str(prop.UID) == str(prop2.UID) and value != value2:
860
                                    if not match:
861
                                        spec_break.extend([line_end, connector.connectedItem])
862
                                        match = True
863
                                    spec_break.append([prop.Attribute, value, value2])
864
                        if match:
865
                            spec_breaks.append(spec_break)
866

    
867
            if spec_breaks:
868
                # check duplication
869
                dupl = set()
870
                for i in range(len(spec_breaks)):
871
                    for j in range(len(spec_breaks)):
872
                        if i == j:
873
                            continue
874
                        else:
875
                            setI = set([spec_breaks[i][0], spec_breaks[i][1]])
876
                            setJ = set([spec_breaks[j][0], spec_breaks[j][1]])
877
                            if not (setI - setJ):
878
                                index = [i, j]
879
                                index.sort()
880
                                index = tuple(index)
881
                                dupl.add(index)
882
                dupl = list(set([(indexSet[1]) for indexSet in list(dupl)]))
883
                dupl.sort(reverse=True)
884
                for index in dupl:
885
                    spec_breaks.pop(index)
886
                # up to here
887

    
888
                spec_break_items = []
889
                for spec in spec_breaks:
890
                    dupl = False
891
                    for freezed in freezed_spec_breaks:
892
                        freezed_attrs = freezed.getAttributes()
893
                        up = [attr.AssocItem for attr in freezed_attrs if attr.Attribute == 'UpStream']
894
                        down = [attr.AssocItem for attr in freezed_attrs if attr.Attribute == 'DownStream']
895
                        if up and down:
896
                            if (up[0] is spec[0] and down[0] is spec[1]) or (
897
                                    up[0] is spec[1] and down[0] is spec[0]):
898
                                dupl = True
899
                                break
900
                    if dupl:
901
                        continue
902

    
903
                    for connector in spec[0].connectors:
904
                        if connector.connectedItem is spec[1]:
905
                            spec_break = SymbolSvgItem.createItem(symbol.getType(), None, svgFilePath)
906
                            pt = [40 + connector.center()[0] - float(symbol.getOriginalPoint().split(',')[0]),
907
                                  60 + connector.center()[1] - float(symbol.getOriginalPoint().split(',')[1])]
908
                            origin = [0, 0]
909
                            if 2 == len(symbol.getOriginalPoint().split(',')):
910
                                tokens = symbol.getOriginalPoint().split(',')
911
                                origin = [pt[0] + float(tokens[0]), pt[1] + float(tokens[1])]
912
                            spec_break.buildItem(svgFileName, symbol.getType(), 3.14, pt,
913
                                                 [spec_break.boundingRect().width(),
914
                                                  spec_break.boundingRect().height()], origin, [],
915
                                                 symbol.getBaseSymbol(), symbol.getAdditionalSymbol(),
916
                                                 symbol.getHasInstrumentLabel())
917

    
918
                            attrs = spec_break.getAttributes()
919
                            for key in attrs.keys():
920
                                if key.Attribute == 'UpStream':
921
                                    attrs[key] = str(spec[0])
922
                                    spec_break.add_assoc_item(spec[0], key.AttrAt, force=True)
923
                                    key.AssocItem = spec[0]
924
                                elif key.Attribute == 'DownStream':
925
                                    attrs[key] = str(spec[1])
926
                                    spec_break.add_assoc_item(spec[1], key.AttrAt, force=True)
927
                                    key.AssocItem = spec[1]
928

    
929
                            for attr, value, value2 in spec[2:]:
930
                                for full in specBreakAttrsFull:
931
                                    if full.Attribute == attr:
932
                                        attrs[full] = [value, value2]
933

    
934
                            # find label text for spec break line
935
                            stream_line = [spec[0], spec[1]]
936
                            stream_track = [spec[1], spec[0]]
937
                            stream_res = [False, False]
938
                            for index in range(len(stream_line)):
939
                                while True:
940
                                    if type(stream_line[index]) is QEngineeringLineItem:
941
                                        stream_res[index] = True
942
                                        break
943
                                    else:
944
                                        find_next = False
945
                                        connected_count = 0
946
                                        for connectorr in stream_line[index].connectors:
947
                                            connected_count += 1
948
                                            if connectorr.connectedItem and stream_track[
949
                                                index] is not connectorr.connectedItem and stream_line[
950
                                                index].next_connected(stream_track[index],
951
                                                                      connectorr.connectedItem):
952
                                                stream_track[index] = stream_line[index]
953
                                                stream_line[index] = connectorr.connectedItem
954
                                                find_next = True
955
                                                break
956

    
957
                                        if not find_next:
958
                                            # prevent infinite loop
959
                                            if connected_count == 2:
960
                                                for connectorr in stream_line[index].connectors:
961
                                                    if connectorr.connectedItem and not connectorr.connectedItem is \
962
                                                                                        stream_track[index]:
963
                                                        stream_line[index] = connectorr.connectedItem
964
                                                        stream_track[index] = stream_line[index]
965
                                                        find_next = True
966
                                                        break
967
                                                if not find_next:
968
                                                    break
969
                                            else:
970
                                                break
971

    
972
                            if stream_res[0] and stream_res[1]:
973
                                texts = [item for item in worker.scene.items() if
974
                                         type(item) is QEngineeringTextItem and item.owner is None]
975
                                positioning = False
976

    
977
                                for attr, value, value2 in spec[2:]:
978
                                    up_texts = [text for text in texts if text.text() == value]
979
                                    down_texts = [text for text in texts if text.text() == value2]
980
                                    up_down_texts = [up_texts, down_texts]
981
                                    up_down_find = [None, None]
982

    
983
                                    for index in range(len(up_down_texts)):
984
                                        minDist = sys.maxsize
985

    
986
                                        for up_down_text in up_down_texts[index]:
987
                                            dx = connector.center()[0] - up_down_text.center().x()
988
                                            dy = connector.center()[1] - up_down_text.center().y()
989
                                            dist = (
990
                                                           up_down_text.sceneBoundingRect().height() + up_down_text.sceneBoundingRect().width()) * ratio / 2
991
                                            length = math.sqrt(dx * dx + dy * dy)
992
                                            if length < dist and length < minDist:
993
                                                up_down_find[index] = up_down_text
994

    
995
                                    if up_down_find[0] and up_down_find[1]:
996
                                        for index in range(len(stream_line)):
997
                                            attrs = stream_line[index].getAttributes()
998
                                            for key in attrs.keys():
999
                                                if key.Attribute == attr:
1000
                                                    attrs[key] = up_down_find[index].text()
1001
                                                    key.AssocItem = up_down_find[index]
1002
                                                    stream_line[index].add_assoc_item(up_down_find[index],
1003
                                                                                      key.AttrAt, force=True)
1004
                                                    up_down_find[index].owner = stream_line[index]
1005
                                                    break
1006

    
1007
                                        if not positioning:
1008
                                            # set spec break position between
1009
                                            positioning = True
1010
                                            new_x = round(
1011
                                                (up_down_find[0].center().x() + up_down_find[1].center().x()) / 2)
1012
                                            new_y = round(
1013
                                                (up_down_find[0].center().y() + up_down_find[1].center().y()) / 2)
1014
                                            spec_break.loc = [new_x - spec_break.symbolOrigin[0],
1015
                                                              new_y - spec_break.symbolOrigin[1]]
1016
                                            spec_break.origin = [new_x, new_y]
1017

    
1018
                                            pivot = None
1019
                                            for connector in spec[0].connectors:
1020
                                                if connector.connectedItem is spec[1]:
1021
                                                    pivot = connector.sceneBoundingRect().center()
1022
                                                    break
1023

    
1024
                                            if abs(up_down_find[0].center().x() - up_down_find[
1025
                                                1].center().x()) < abs(
1026
                                                up_down_find[0].center().y() - up_down_find[1].center().y()):
1027
                                                if new_x > pivot.x():
1028
                                                    spec_break.angle = 1.57
1029
                                                else:
1030
                                                    spec_break.angle = 4.71239
1031
                                            else:
1032
                                                if new_y > pivot.y():
1033
                                                    spec_break.angle = 3.14
1034
                                                else:
1035
                                                    spec_break.angle = 0
1036

    
1037
                                            # make show prop true
1038
                                            spec_break.set_property('Show', True)
1039

    
1040
                            spec_break_items.append(spec_break)
1041

    
1042
                for spec_break_item in spec_break_items:
1043
                    spec_break_item.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
1044
                    spec_break_item.addSvgItemToScene(worker.scene)
1045

    
1046
                spec_break_items.extend(freezed_spec_breaks)
1047
    except Exception as ex:
1048
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1049
                                                       sys.exc_info()[-1].tb_lineno)
1050
        worker.display_message.emit(message)
1051

    
1052

    
1053
def make_flow_mark(worker, lines):
1054
    ''' make flow mark '''
1055

    
1056
    try:
1057
        docdata = AppDocData.instance()
1058

    
1059
        for line in lines:
1060
            line.flowMark = None
1061
            # line.update_arrow()
1062

    
1063
        configs = docdata.getConfigs('Flow Mark', 'Position')
1064
        position = int(configs[0].value) if 1 == len(configs) else 100
1065
        configs = docdata.getConfigs('Flow Mark', 'Length')
1066
        length = int(configs[0].value) if 1 == len(configs) else 200
1067

    
1068
        for line in [line for line in lines if line.is_piping(True)]:
1069
            line.update_flow_mark(position, length)
1070
            line.update_arrow()
1071
        # for lineNo in docdata.tracerLineNos:
1072
        #    lineNo.update_flow_mark(position, length)
1073

    
1074
    except Exception as ex:
1075
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1076
                                                       sys.exc_info()[-1].tb_lineno)
1077
        worker.display_message.emit(message)
1078

    
1079

    
1080
def sort_run_flow(worker):
1081
    ''' sort runs '''
1082

    
1083
    from EngineeringLineTracerRunItem import QEngineeringLineTracerRunItem
1084

    
1085
    try:
1086
        docdata = AppDocData.instance()
1087

    
1088
        fixed_run_infos = []  # QEngineeringLineTracerRunItem s
1089
        waiting_run_infos = []  # QEngineeringLineTracerRunItem s
1090
        runs = []
1091
        # first step : make fixed run using symbol info
1092
        for lineNo in [lineNo for lineNo in docdata.tracerLineNos if len(lineNo.runs) > 0]:
1093
            not_trim = True if type(lineNo) is QEngineeringLineNoTextItem else False
1094
            run_index = 0
1095
            for run in lineNo.runs:
1096
                not_secondary = True if run_index is 0 else False
1097

    
1098
                # if from and to info already was entered, fix run flow
1099
                if hasattr(lineNo, '_fixed') and lineNo._fixed and not_secondary:
1100
                    fixed_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1101
                    run_index += 1
1102
                    continue
1103

    
1104
                reference_symbols = [item for item in run.items if
1105
                                     issubclass(type(item), SymbolSvgItem) and item.has_in_out_connector()]
1106
                # runs can know flow directly
1107
                if len(run.items) > 1 and len(reference_symbols) > 0:
1108
                    for reference_symbol in reference_symbols:
1109
                        # place at first
1110
                        if reference_symbol is run.items[0]:
1111
                            if len([connector_index for connector_index in reference_symbol.in_out_connector[0] \
1112
                                    if reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0:
1113
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1114
                                info.reverse()
1115
                                fixed_run_infos.append(info)
1116
                                break
1117
                            elif len([connector_index for connector_index in reference_symbol.in_out_connector[1] \
1118
                                      if
1119
                                      reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0:
1120
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1121
                                fixed_run_infos.append(info)
1122
                                break
1123
                        # place at last
1124
                        elif reference_symbol is run.items[-1]:
1125
                            if len([connector_index for connector_index in reference_symbol.in_out_connector[1] \
1126
                                    if
1127
                                    reference_symbol.connectors[connector_index].connectedItem is run.items[-2]]) > 0:
1128
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1129
                                info.reverse()
1130
                                fixed_run_infos.append(info)
1131
                                break
1132
                            elif len([connector_index for connector_index in reference_symbol.in_out_connector[0] \
1133
                                      if
1134
                                      reference_symbol.connectors[connector_index].connectedItem is run.items[-2]]) > 0:
1135
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1136
                                fixed_run_infos.append(info)
1137
                                break
1138
                        # place at middle
1139
                        else:
1140
                            in_items = [reference_symbol.connectors[connector_index].connectedItem for connector_index
1141
                                        in reference_symbol.in_out_connector[0] \
1142
                                        if reference_symbol.connectors[connector_index].connectedItem in run.items]
1143
                            out_items = [reference_symbol.connectors[connector_index].connectedItem for connector_index
1144
                                         in reference_symbol.in_out_connector[1] \
1145
                                         if reference_symbol.connectors[connector_index].connectedItem in run.items]
1146
                            in_item = in_items[0] if in_items else None
1147
                            out_item = out_items[0] if out_items else None
1148
                            if in_item and out_item:
1149
                                in_item_index = run.items.index(in_item)
1150
                                out_item_index = run.items.index(out_item)
1151
                                if out_item_index < in_item_index:
1152
                                    info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1153
                                    info.reverse()
1154
                                    fixed_run_infos.append(info)
1155
                                    break
1156
                                else:
1157
                                    info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1158
                                    fixed_run_infos.append(info)
1159
                                    break
1160
                # only symbol runs doesn't need flow
1161
                elif len(run.items) is 1 and issubclass(type(run.items[0]), SymbolSvgItem):
1162
                    runs.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1163
                # runs can't know flow directly
1164
                else:
1165
                    waiting_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1166

    
1167
                run_index += 1
1168

    
1169
        QApplication.processEvents()
1170
        # second step : determine waiting run flow, connected by point
1171
        remain_count_past = len(waiting_run_infos)
1172
        while True:
1173
            remain_count = 0
1174
            for run_index in reversed(range(len(waiting_run_infos))):
1175
                waiting_run_info = waiting_run_infos[run_index]
1176
                waiting_run = waiting_run_info.run
1177

    
1178
                find = False
1179
                for fixed_run_info in fixed_run_infos:
1180
                    fixed_run = fixed_run_info.run
1181
                    if len(waiting_run.items) > 1 and len(fixed_run.items) > 1:
1182
                        # waiting_run and fix_run have items more than 2
1183
                        if waiting_run.items[0].is_connected(fixed_run.items[0]):
1184
                            waiting_run_info.reverse()
1185
                            fixed_run_infos.append(waiting_run_info)
1186
                            waiting_run_infos.pop(run_index)
1187
                            find = True
1188
                            break
1189
                        elif waiting_run.items[0].is_connected(fixed_run.items[-1]):
1190
                            fixed_run_infos.append(waiting_run_info)
1191
                            waiting_run_infos.pop(run_index)
1192
                            find = True
1193
                            break
1194
                        elif waiting_run.items[-1].is_connected(fixed_run.items[-1]):
1195
                            waiting_run_info.reverse()
1196
                            fixed_run_infos.append(waiting_run_info)
1197
                            waiting_run_infos.pop(run_index)
1198
                            find = True
1199
                            break
1200
                        elif waiting_run.items[-1].is_connected(fixed_run.items[0]):
1201
                            fixed_run_infos.append(waiting_run_info)
1202
                            waiting_run_infos.pop(run_index)
1203
                            find = True
1204
                            break
1205
                    elif len(fixed_run.items) > 1:
1206
                        # waiting_run is single line
1207
                        if waiting_run.items[0].connectors[0].connectedItem is fixed_run.items[0]:
1208
                            waiting_run_info.reverse()
1209
                            fixed_run_infos.append(waiting_run_info)
1210
                            waiting_run_infos.pop(run_index)
1211
                            find = True
1212
                            break
1213
                        elif waiting_run.items[0].connectors[0].connectedItem is fixed_run.items[-1]:
1214
                            fixed_run_infos.append(waiting_run_info)
1215
                            waiting_run_infos.pop(run_index)
1216
                            find = True
1217
                            break
1218
                        elif waiting_run.items[0].connectors[1].connectedItem is fixed_run.items[-1]:
1219
                            waiting_run_info.reverse()
1220
                            fixed_run_infos.append(waiting_run_info)
1221
                            waiting_run_infos.pop(run_index)
1222
                            find = True
1223
                            break
1224
                        elif waiting_run.items[0].connectors[1].connectedItem is fixed_run.items[0]:
1225
                            fixed_run_infos.append(waiting_run_info)
1226
                            waiting_run_infos.pop(run_index)
1227
                            find = True
1228
                            break
1229
                    else:
1230
                        if waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1231
                            0].connectedItem is waiting_run.items[0]:
1232
                            waiting_run.reverse()
1233
                            fixed_run_infos.append(waiting_run_info)
1234
                            waiting_run_infos.pop(run_index)
1235
                            find = True
1236
                            break
1237
                        elif waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1238
                            1].connectedItem is waiting_run.items[0]:
1239
                            fixed_run_infos.append(waiting_run_info)
1240
                            waiting_run_infos.pop(run_index)
1241
                            find = True
1242
                            break
1243
                        elif waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1244
                            1].connectedItem is waiting_run.items[0]:
1245
                            waiting_run.reverse()
1246
                            fixed_run_infos.append(waiting_run_info)
1247
                            waiting_run_infos.pop(run_index)
1248
                            find = True
1249
                            break
1250
                        elif waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1251
                            0].connectedItem is waiting_run.items[0]:
1252
                            fixed_run_infos.append(waiting_run_info)
1253
                            waiting_run_infos.pop(run_index)
1254
                            find = True
1255
                            break
1256
                if not find:
1257
                    remain_count += 1
1258

    
1259
            if remain_count_past == remain_count:
1260
                break
1261
            else:
1262
                remain_count_past = remain_count
1263

    
1264
        QApplication.processEvents()
1265
        # third step : body connected run sort, not split
1266
        remain_count_past = len(waiting_run_infos)
1267
        while True:
1268
            remain_count = 0
1269
            for run_index in reversed(range(len(waiting_run_infos))):
1270
                waiting_run_info = waiting_run_infos[run_index]
1271
                waiting_run = waiting_run_info.run
1272

    
1273
                find = False
1274
                for fixed_run_info in fixed_run_infos:
1275
                    fixed_run = fixed_run_info.run
1276
                    if len(waiting_run.items) > 1 and len(fixed_run.items) > 1 and type(
1277
                            waiting_run.items[0]) is QEngineeringLineItem and type(
1278
                        waiting_run.items[-1]) is QEngineeringLineItem:
1279
                        if waiting_run.items[0].connectors[0].connectedItem in fixed_run.items and \
1280
                                waiting_run.items[-1].connectors[1].connectedItem in fixed_run.items:
1281
                            if fixed_run.items.index(
1282
                                    waiting_run.items[0].connectors[0].connectedItem) > fixed_run.items.index(
1283
                                waiting_run.items[-1].connectors[1].connectedItem):
1284
                                waiting_run.reverse()
1285
                                fixed_run_infos.append(waiting_run_info)
1286
                                waiting_run_infos.pop(run_index)
1287
                                find = True
1288
                                break
1289
                            else:
1290
                                fixed_run_infos.append(waiting_run_info)
1291
                                waiting_run_infos.pop(run_index)
1292
                                find = True
1293
                                break
1294

    
1295
                if not find:
1296
                    remain_count += 1
1297

    
1298
            if remain_count_past == remain_count:
1299
                break
1300
            else:
1301
                remain_count_past = remain_count
1302

    
1303
        # fourth step : body connected split run or split owner run sort
1304
        fixed_merged_run_infos = []  # [[run_info1, run_info2, ... in order], [merged run with items]]
1305
        waiting_merged_run_infos = []  # [[run_info1, run_info2, ... in order], [merged run with items]]
1306

    
1307
        QApplication.processEvents()
1308
        # waiting run merge and sort
1309
        consumed_count_past = 0
1310
        for index1 in range(len(waiting_run_infos)):
1311
            if waiting_run_infos[index1].consumed:
1312
                continue
1313
            for index2 in range(len(waiting_run_infos)):
1314
                if index1 == index2 or waiting_run_infos[index2].consumed:
1315
                    continue
1316

    
1317
                result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1318
                    waiting_run_infos[index1], waiting_run_infos[index2])
1319
                if result:
1320
                    waiting_run_infos[index1].consumed = True
1321
                    waiting_run_infos[index2].consumed = True
1322
                    consumed_count_past += 2
1323
                    if header:
1324
                        waiting_merged_run_infos.append(
1325
                            [[waiting_run_infos[index2], waiting_run_infos[index1]], merged_run_info])
1326
                    else:
1327
                        waiting_merged_run_infos.append(
1328
                            [[waiting_run_infos[index1], waiting_run_infos[index2]], merged_run_info])
1329
                    break
1330

    
1331
        QApplication.processEvents()
1332
        while True:
1333
            consumed_count = 0
1334
            for index in range(len(waiting_run_infos)):
1335
                if waiting_run_infos[index].consumed:
1336
                    consumed_count += 1
1337
                    continue
1338

    
1339
                for waiting_merged_run_info in waiting_merged_run_infos:
1340
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1341
                        waiting_merged_run_info[1], waiting_run_infos[index])
1342
                    if result:
1343
                        waiting_run_infos[index].consumed = True
1344
                        consumed_count += 1
1345
                        if header:
1346
                            waiting_merged_run_info[0].insert(0, waiting_run_infos[index])
1347
                            waiting_merged_run_info[1] = merged_run_info
1348
                        else:
1349
                            waiting_merged_run_info[0].append(waiting_run_infos[index])
1350
                            waiting_merged_run_info[1] = merged_run_info
1351
                        break
1352

    
1353
            if consumed_count_past == consumed_count:
1354
                break
1355
            else:
1356
                consumed_count_past = consumed_count
1357

    
1358
        QApplication.processEvents()
1359
        while True:
1360
            merged = False
1361
            for index1 in range(len(waiting_merged_run_infos)):
1362
                waiting_merged_run_info1 = waiting_merged_run_infos[index1]
1363
                for index2 in range(len(waiting_merged_run_infos)):
1364
                    if index1 == index2:
1365
                        continue
1366
                    waiting_merged_run_info2 = waiting_merged_run_infos[index2]
1367
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1368
                        waiting_merged_run_info1[1], waiting_merged_run_info2[1], sort_connect_info=False)
1369
                    if result:
1370
                        if header:
1371
                            if reverse:
1372
                                for waiting_run_info in waiting_merged_run_info2[0]:
1373
                                    waiting_run_info.reverse()
1374
                                waiting_merged_run_info2[0].reverse()
1375
                                waiting_merged_run_info2[0].extend(waiting_merged_run_info1[0])
1376
                            waiting_merged_run_info1[0] = waiting_merged_run_info2[0]
1377
                            waiting_merged_run_info1[1] = merged_run_info
1378
                        else:
1379
                            if reverse:
1380
                                for waiting_run_info in waiting_merged_run_info2[0]:
1381
                                    waiting_run_info.reverse()
1382
                                waiting_merged_run_info2[0].reverse()
1383
                            waiting_merged_run_info1[0].extend(waiting_merged_run_info2[0])
1384
                            waiting_merged_run_info1[1] = merged_run_info
1385
                        merged = True
1386
                        break
1387
                if merged:
1388
                    waiting_merged_run_infos.pop(index2)
1389
                    break
1390
            if not merged:
1391
                break
1392

    
1393
        QApplication.processEvents()
1394
        # fixed run merge
1395
        consumed_count_past = 0
1396
        for index1 in range(len(fixed_run_infos)):
1397
            if fixed_run_infos[index1].consumed:
1398
                continue
1399
            for index2 in range(len(fixed_run_infos)):
1400
                if index1 == index2 or fixed_run_infos[index2].consumed:
1401
                    continue
1402

    
1403
                result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1404
                    fixed_run_infos[index1], fixed_run_infos[index2], sort=False)
1405
                if result:
1406
                    fixed_run_infos[index1].consumed = True
1407
                    fixed_run_infos[index2].consumed = True
1408
                    consumed_count_past += 2
1409
                    if header:
1410
                        fixed_merged_run_infos.append(
1411
                            [[fixed_run_infos[index2], fixed_run_infos[index1]], merged_run_info])
1412
                    else:
1413
                        fixed_merged_run_infos.append(
1414
                            [[fixed_run_infos[index1], fixed_run_infos[index2]], merged_run_info])
1415
                    break
1416

    
1417
        QApplication.processEvents()
1418
        while True:
1419
            consumed_count = 0
1420
            for index in range(len(fixed_run_infos)):
1421
                if fixed_run_infos[index].consumed:
1422
                    consumed_count += 1
1423
                    continue
1424

    
1425
                for fixed_merged_run_info in fixed_merged_run_infos:
1426
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1427
                        fixed_merged_run_info[1], fixed_run_infos[index], sort=False)
1428
                    if result:
1429
                        fixed_run_infos[index].consumed = True
1430
                        consumed_count += 1
1431
                        if header:
1432
                            fixed_merged_run_info[0].insert(0, fixed_run_infos[index])
1433
                            fixed_merged_run_info[1] = merged_run_info
1434
                        else:
1435
                            fixed_merged_run_info[0].append(fixed_run_infos[index])
1436
                            fixed_merged_run_info[1] = merged_run_info
1437
                        break
1438

    
1439
            if consumed_count_past == consumed_count:
1440
                break
1441
            else:
1442
                consumed_count_past = consumed_count
1443

    
1444
        QApplication.processEvents()
1445
        while True:
1446
            merged = False
1447
            for index1 in range(len(fixed_merged_run_infos)):
1448
                fixed_merged_run_info1 = fixed_merged_run_infos[index1]
1449
                for index2 in range(len(fixed_merged_run_infos)):
1450
                    if index1 == index2:
1451
                        continue
1452
                    fixed_merged_run_info2 = fixed_merged_run_infos[index2]
1453
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1454
                        fixed_merged_run_info1[1], fixed_merged_run_info2[1], sort=False)
1455
                    if result:
1456
                        if header:
1457
                            fixed_merged_run_info1[0] = fixed_merged_run_info2[0]
1458
                            fixed_merged_run_info1[1] = merged_run_info
1459
                        else:
1460
                            fixed_merged_run_info1[0].extend(fixed_merged_run_info2[0])
1461
                            fixed_merged_run_info1[1] = merged_run_info
1462
                        merged = True
1463
                        break
1464
                if merged:
1465
                    fixed_merged_run_infos.pop(index2)
1466
                    break
1467
            if not merged:
1468
                break
1469

    
1470
        for fixed_run_info in fixed_run_infos:
1471
            if fixed_run_info.consumed:
1472
                continue
1473
            else:
1474
                fixed_merged_run_infos.append([[fixed_run_info], fixed_run_info])
1475

    
1476
        QApplication.processEvents()
1477
        # sort merged waiting runs by using merged fixed runs
1478
        remain_count_past = len(waiting_merged_run_infos)
1479
        while True:
1480
            remain_count = 0
1481
            for run_index in reversed(range(len(waiting_merged_run_infos))):
1482
                waiting_run_info = waiting_merged_run_infos[run_index][0]
1483
                waiting_merged_run = waiting_merged_run_infos[run_index][1].run
1484

    
1485
                find = False
1486
                for fixed_merged_run_info in fixed_merged_run_infos:
1487
                    fixed_merged_run = fixed_merged_run_info[1].run
1488
                    if len(waiting_merged_run.items) > 1 and len(fixed_merged_run.items) > 1 and type(
1489
                            waiting_merged_run.items[0]) is QEngineeringLineItem and type(
1490
                        waiting_merged_run.items[-1]) is QEngineeringLineItem:
1491
                        if waiting_merged_run.items[0].connectors[0].connectedItem in fixed_merged_run.items and \
1492
                                waiting_merged_run.items[-1].connectors[1].connectedItem in fixed_merged_run.items:
1493
                            if fixed_merged_run.items.index(waiting_merged_run.items[0].connectors[
1494
                                                                0].connectedItem) > fixed_merged_run.items.index(
1495
                                waiting_merged_run.items[-1].connectors[1].connectedItem):
1496
                                for waiting_run_info in waiting_merged_run_infos[run_index][0]:
1497
                                    waiting_run_info.reverse()
1498
                                waiting_merged_run_infos[run_index][0].reverse()
1499
                                reverse_order = []
1500
                                for index in reversed(range(len(waiting_merged_run_infos[run_index][1].run.items))):
1501
                                    reverse_order.append(waiting_merged_run_infos[run_index][1].run.items[index])
1502
                                waiting_merged_run_infos[run_index][1].run.items = reverse_order
1503
                                fixed_merged_run_infos.append(waiting_merged_run_infos[run_index])
1504
                                waiting_merged_run_infos.pop(run_index)
1505
                                find = True
1506
                                break
1507
                            else:
1508
                                fixed_merged_run_infos.append(waiting_merged_run_infos[run_index])
1509
                                waiting_merged_run_infos.pop(run_index)
1510
                                find = True
1511
                                break
1512

    
1513
                if not find:
1514
                    remain_count += 1
1515

    
1516
            if remain_count_past == remain_count:
1517
                break
1518
            else:
1519
                remain_count_past = remain_count
1520
    except Exception as ex:
1521
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1522
                                                       sys.exc_info()[-1].tb_lineno)
1523
        worker.display_message.emit(message)
1524

    
1525
async def connectAttrImpl_inst(worker, update_line_type, update_flow_mark, update_spec):
1526
    from App import App
1527
    import uuid
1528
    from LineNoTracer import LineNoTracer
1529
    from AppDocData import AppDocData
1530
    from EngineeringAbstractItem import QEngineeringAbstractItem
1531
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1532
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
1533
    from EngineeringReducerItem import QEngineeringReducerItem
1534
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
1535
    from QEngineeringOPCItem import QEngineeringOPCItem
1536
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1537
    from EngineeringVendorItem import QEngineeringVendorItem
1538
    from EngineeringEndBreakItem import QEngineeringEndBreakItem
1539
    from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
1540
    from EngineeringReservedWordTextItem import QEngineeringReservedWordTextItem
1541
    from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1542
    from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1543
    from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
1544
    from QEngineeringTagNoTextItem import QEngineeringTagNoTextItem
1545
    from EngineeringErrorItem import QEngineeringErrorItem
1546
    from EngineeringTextItem import QEngineeringTextItem
1547
    from SpecialItemTypesDialog import SpecialItemTracer
1548

    
1549
    try:
1550
        docdata = AppDocData.instance()
1551
        worker.display_message.emit('Initializing...')
1552

    
1553
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
1554
        for item in ALL_ITEM:
1555
            item.setVisible(False)
1556
        QApplication.processEvents()
1557

    
1558
        symbols = []
1559
        lines = [item for item in worker.scene.items() if type(item) is QEngineeringLineItem]
1560
        lineNos = []
1561
        equip_packages = []
1562
        end_breaks = []
1563

    
1564
        for error_item in [item for item in worker.scene.items() if type(item) is QEngineeringErrorItem]:
1565
            error_item.transfer.onRemoved.emit(error_item)
1566

    
1567
        for end_break in [item for item in worker.scene.items() if type(item) is QEngineeringEndBreakItem]:
1568
            if not end_break.prop('Freeze'):
1569
                end_break.transfer.onRemoved.emit(end_break)
1570
            else:
1571
                end_breaks.append(end_break)
1572

    
1573
        QApplication.processEvents()
1574

    
1575
        for item in worker.scene.items():
1576
            if issubclass(type(item), SymbolSvgItem) and not (type(item) is QEngineeringErrorItem) and not (
1577
                    type(item) is QEngineeringUnknownItem) and item.type != 'Notes' and not (
1578
                    type(item) is QEngineeringEndBreakItem):
1579
                symbols.append(item)
1580
            elif type(item) is QEngineeringLineNoTextItem:
1581
                lineNos.append(item)
1582
            elif issubclass(type(item), QEngineeringTextItem):
1583
                item.owner = None
1584

    
1585
        QApplication.processEvents()
1586

    
1587
        # trace connected items
1588
        docdata._connected_items_lists.clear()
1589
        connected_items_lists = docdata._connected_items_lists
1590

    
1591
        items = symbols + lines
1592

    
1593
        while items:
1594
            connected_items_list = [items[0]]
1595
            connected_items_count = 0
1596
            while True:
1597
                if connected_items_count == len(connected_items_list):
1598
                    break
1599
                else:
1600
                    connected_items_count = len(connected_items_list)
1601

    
1602
                for item in items:
1603
                    if item in connected_items_list:
1604
                        continue
1605
                    else:
1606
                        matches = [conn for conn in item.connectors if conn.connectedItem and conn.connectedItem in connected_items_list]
1607
                        if matches:
1608
                            connected_items_list.append(item)
1609
                            break
1610

    
1611
            for item in connected_items_list:
1612
                items.remove(item)
1613
            
1614
            connected_items_lists.append(connected_items_list)
1615
        # up to here
1616

    
1617
        # connect attribute
1618
        worker.display_message.emit('Connecting Attribute...')
1619
        QApplication.processEvents()
1620
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
1621
        for symbol in symbols:
1622
            try:
1623
                symbol.connectAttribute(texts)
1624
            except Exception as ex:
1625
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1626
                                                               sys.exc_info()[-1].tb_lineno)
1627
                worker.display_message.emit(message)
1628

    
1629
        """ try to connect label to valve """
1630
        labels = [symbol for symbol in symbols if symbol.iType == 19]  # Labels - Symbol => Instrument
1631
        valves = [symbol for symbol in symbols if
1632
                  symbol.iType == 34 or symbol.iType == 17 or symbol.iType == 22]  # Specialty Components, In-Line, Relief Devices
1633
        for label in labels:
1634
            label.connectAttribute(valves, clear=False)
1635

    
1636
        # for slope, etc.., symbol to line
1637
        labels = [symbol for symbol in symbols if symbol.iType == 30]  # Labels - Line => Piping
1638
        for label in labels:
1639
            label.connectAttribute(lines, clear=False)
1640

    
1641
        QApplication.processEvents()
1642
        for symbol in symbols:
1643
            for assoc in symbol.associations():
1644
                if assoc.owner is None:
1645
                    assoc.owner = symbol
1646

    
1647
        """ try to find text item's owner """
1648
        texts = [item for item in worker.scene.items() if type(item) is QEngineeringReservedWordTextItem]
1649
        for text in texts:
1650
            text.findOwner(lines)
1651

    
1652
        QApplication.processEvents()
1653
        # restore and save manual edited attr's text item
1654
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
1655
        targetText = []
1656
        for text in texts:
1657
            found = False
1658
            for symbol in symbols + lines:
1659
                for attr in list(symbol.attrs.keys()):
1660
                    # skip freezed attr or already consumed by symbol
1661
                    if attr.AssocItem == text:
1662
                        text.owner = symbol
1663
                        found = True
1664
                        break
1665
                    # restore missing attr
1666
                    elif type(attr.AssocItem) is str or type(attr.AssocItem) is uuid.UUID:
1667
                        if str(attr.AssocItem) == str(text.uid):
1668
                            symbol.add_assoc_item(text)
1669
                            attr.AssocItem = text
1670
                            text.owner = symbol
1671
                            found = True
1672
                        break
1673
                if found: break
1674
            if not found and (
1675
                    type(text) is QEngineeringSizeTextItem or type(text) is QEngineeringValveOperCodeTextItem or \
1676
                    type(text) is QEngineeringTagNoTextItem or type(text) is QEngineeringTextItem):
1677
                targetText.append(text)
1678

    
1679
        worker.need_update_texts = []
1680
        targetText = sorted(targetText, key=lambda param: len(param.text()), reverse=True)
1681
        for symbol in symbols + equip_packages:
1682
            symbol._skip = False
1683
        for text in targetText:
1684
            ret = text.findOwner(symbols) if type(text) is not QEngineeringTagNoTextItem else text.findOwner(
1685
                symbols + equip_packages)
1686
            if ret:
1687
                worker.need_update_texts.append([text, ret])
1688

    
1689
        QApplication.processEvents()
1690

    
1691
        worker.updateProgress.emit(tracer.maxValue)
1692

    
1693
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
1694
        for item in ALL_ITEM:
1695
            item.setVisible(True)
1696

    
1697
    except Exception as ex:
1698
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1699
                                                       sys.exc_info()[-1].tb_lineno)
1700
        worker.display_message.emit(message)
1701
    except:
1702
        (type1, value, traceback) = sys.exc_info()
1703
        sys.excepthook(type1, value, traceback)
클립보드 이미지 추가 (최대 크기: 500 MB)