프로젝트

일반

사용자정보

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

hytos / HYTOS / HYTOS / MainWindow.py @ bb3cbaf8

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

1
# -*- coding: utf-8 -*-
2
""" This is MainWindow module """
3

    
4
import sys
5
import os
6
import subprocess
7
from functools import partial
8

    
9
# from openpyxl.drawing import colors
10
from FreezeTableWidget import FreezeTableWidget
11

    
12
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
13
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands')
14
import CreateCommand
15
import AreaZoomCommand
16
import PlaceStreamlineCommand
17
import LeftAlignmentCommand, RightAlignmentCommand, UpAlignmentCommand, DownAlignmentCommand
18

    
19
from PyQt5.QtCore import *
20
from PyQt5.QtGui import *
21
from PyQt5.QtWidgets import *
22
from PyQt5.QtSvg import *
23

    
24
import MainWindow_UI
25
import QtImageViewer
26
from SingletonInstance import SingletonInstane
27

    
28
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes')
29
from SymbolSvgItem import SymbolSvgItem
30

    
31
from EngineeringErrorItem import QEngineeringErrorItem
32
from EngineeringStreamlineItem import QEngineeringStreamlineItem
33
from AppDocData import *
34
import SymbolTreeWidget
35
import uuid
36
import math
37

    
38

    
39
def is_blank(s):
40
    return not (s and s.strip())
41

    
42

    
43
def is_not_blank(s):
44
    return bool(s and s.strip())
45

    
46

    
47
def is_float(s):
48
    try:
49
        if s:
50
            float(s)
51
            return True
52
        else:
53
            return False
54
    except ValueError:
55
        return False
56

    
57

    
58
def set_item_properties(name: str, alignment, backgroundcolor=None, foregroundcolor=None) -> QStandardItem:
59
    if name is None:
60
        name = ''
61

    
62
    item = QStandardItem(str(name))
63
    item.setTextAlignment(alignment)
64
    if backgroundcolor:
65
        item.setBackground(backgroundcolor)
66
    if foregroundcolor:
67
        item.setForeground(foregroundcolor)  # QBrush(QColor(255, 0, 0)))
68
    return item
69

    
70

    
71
def convert_to_fixed_point(value):
72
    if is_float(str(value)):
73
        tokens = f"{float(value):.10f}".split('.')
74
        if len(tokens) == 2:
75
            # 소수점 아래가 있을 경우 소수점 아래의 trailing zero를 제거한다.
76
            if is_blank(tokens[1].rstrip('0')):
77
                return tokens[0]
78
            else:
79
                tokens[1] = tokens[1].rstrip('0')
80
                return '.'.join(tokens)
81
        else:
82
            return tokens[0]
83
    else:
84
        return value
85

    
86

    
87
class ItemDelegate(QStyledItemDelegate):
88
    def createEditor(self, parent, option, index):
89
        """
90
        create a editor for user input
91
        @param parent:
92
        @param option:
93
        @param index:
94
        @return:
95
        """
96
        editor = None
97
        if index.column() >= 2:
98
            if index.row() == 3:
99
                editor = QComboBox(parent)
100
                editor.addItems(['Liquid', 'Vapor', 'Mixed'])
101
            elif index.row() <= 23:
102
                editor = QLineEdit(parent)
103
                if index.row() in [4, 5]:
104
                    validator = QDoubleValidator(parent)
105
                    editor.setValidator(validator)
106

    
107
                value = editor.text()
108
                if type(value) is float:
109
                    validator = QDoubleValidator(parent)
110
                    editor.setValidator(validator)
111

    
112
        return editor
113

    
114
    def destroyEditor(self, editor, index):
115
        """
116
        change background if value is changed
117
        @param editor:
118
        @param index:
119
        @return:
120
        """
121
        editor.blockSignals(True)
122
        if type(editor) is QComboBox:
123
            changed = editor.currentText()
124
        else:
125
            changed = editor.text()
126
        item = editor.parentWidget().parentWidget().model().item(index.row(), index.column())
127
        value = item.data(Qt.UserRole)
128

    
129
        value_changed = False
130
        if type(value) is float and changed:
131
            value_changed = float(changed) != value
132
            item.setBackground(Qt.magenta if value_changed else Qt.white)
133
        else:
134
            value_changed = changed != value
135
            item.setBackground(Qt.magenta if value_changed else Qt.white)
136

    
137
        """clear value of other side"""
138
        if value_changed:
139
            if index.row() == 4:
140
                item = editor.parentWidget().parentWidget().model().item(5, index.column())
141
                item.setText('')
142
                item.setBackground(Qt.magenta)
143
            elif index.row() == 5:
144
                item = editor.parentWidget().parentWidget().model().item(4, index.column())
145
                item.setText('')
146
                item.setBackground(Qt.magenta)
147

    
148
        """
149
        if index.row() == 3:
150
            item = editor.parentWidget().parentWidget().item(5, index.column())
151
            if changed in ['Liquid', 'Mixed']:
152
                item.setFlags(item.flags() | Qt.ItemIsEditable)
153
            else:
154
                item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
155
        """
156

    
157
        editor.blockSignals(False)
158

    
159

    
160
class MainWindow(QMainWindow, MainWindow_UI.Ui_MainWindow, SingletonInstane):
161
    """ This is MainWindow class """
162
    DEFAULT_SIZE = QSizeF(1188, 841)
163
    addMessage = pyqtSignal(Enum, str)
164

    
165
    def __init__(self):
166
        """initialize"""
167

    
168
        try:
169
            super(self.__class__, self).__init__()
170
            self.setupUi(self)
171
            self.tableWidgetHMB = FreezeTableWidget(self, 2)
172
            self.horizontalLayoutStreamData.addWidget(self.tableWidgetHMB)
173

    
174
            self._label_mouse = QLabel(self.statusbar)
175
            self._label_mouse.setText(self.tr(f'mouse pos : ({0},{0})'))
176
            self.statusbar.addWidget(self._label_mouse)
177
            self.addMessage.connect(self.onAddMessage)
178

    
179
            self.setMainWindowTitle()
180

    
181
            self.graphicsView = QtImageViewer.QtImageViewer(self)
182
            self.graphicsView.setParent(self.centralwidget)
183
            self.graphicsView.useDefaultCommand()
184
            self.graphicsView.setMouseTracking(True)
185
            self.graphicsView.viewport().installEventFilter(self)
186
            self.verticalLayout.addWidget(self.graphicsView)
187

    
188
            # Add Symbol TreeWidget
189
            self.symbolTreeWidget = SymbolTreeWidget.QSymbolTreeWidget()
190
            self.symbolTreeWidget.header().hide()
191
            self.verticalLayoutSymbolList.addWidget(self.symbolTreeWidget)
192

    
193
            # Initialize Action group
194
            self.actionGroup = QActionGroup(self)
195
            self.actionGroup.addAction(self.actionLine)
196
            self.actionGroup.addAction(self.actionZoom)
197
            self.actionGroup.addAction(self.actionFitWindow)
198
            self.actionGroup.addAction(self.actionSave)
199

    
200
            # connect signals and slots
201
            self.actionGroup.triggered.connect(self.actionGroupTriggered)
202
            self.actionClose.triggered.connect(self.close)
203
            self.actionNew.triggered.connect(self.on_new_drawing)
204
            self.actionOpen.triggered.connect(self.on_open_drawing)
205
            self.actionSave.triggered.connect(self.on_save)
206
            self.actionSave_As.triggered.connect(self.on_save_as)
207
            self.actionUndo.triggered.connect(self.on_undo)
208
            self.actionRedo.triggered.connect(self.on_redo)
209
            self.actionCalculation.triggered.connect(partial(self.calculation, True))
210
            self.actionGenerateReport.triggered.connect(self.on_generate_report)
211
            self.actionLine.triggered.connect(self.onPlaceLine)
212
            self.actionConfiguration.triggered.connect(self.configuration)
213
            self.actionInitialize.triggered.connect(self.onInitializeScene)
214
            self.actionOptions.triggered.connect(self.onOptions)
215
            self.actionZoom.triggered.connect(self.onAreaZoom)
216
            self.actionFitWindow.triggered.connect(self.fitWindow)
217
            self.actionViewConnector.triggered.connect(self.on_view_connector)
218
            self.actionSymbol_Editor.triggered.connect(self.showSymbolEditor)
219
            self.actionPlaceTextBox.triggered.connect(self.on_place_callout_textbox)
220
            self.actionPlaceDimension.triggered.connect(self.on_place_dimension)
221
            self.actionPlaceCloud.triggered.connect(self.on_place_cloud)
222
            self.actionHelp.triggered.connect(self.on_help)
223
            self.actionAbout.triggered.connect(self.on_about)
224
            self.actionReadme.triggered.connect(self.on_readme)
225
            self.toolButton_ClearLog.clicked.connect(self.clearlogs)
226
            self.graphicsView.scene.selectionChanged.connect(self.onSelectionChanged)
227

    
228
            self.actionLeftAlignment.triggered.connect(self.on_left_alignment)
229
            self.actionRightAlignment.triggered.connect(self.on_right_alignment)
230
            self.actionUpAlignment.triggered.connect(self.on_up_alignment)
231
            self.actionDownAlignment.triggered.connect(self.on_down_alignment)
232

    
233
            self.treeViewDrawingList.setContextMenuPolicy(Qt.CustomContextMenu)
234
            self.treeViewDrawingList.customContextMenuRequested.connect(self.open_context_menu)
235
            self.treeViewDrawingList.doubleClicked.connect(self.open_selected_drawing)
236

    
237
            delegate = ItemDelegate(self.tableWidgetHMB)
238
            self.tableWidgetHMB.setItemDelegate(delegate)
239
            self.toolButtonSyncStreamData.clicked.connect(self.on_sync_stream_data)
240
            self.toolButtonRevertStreamData.clicked.connect(self.load_HMB)
241
            self.initTableWidgetHMB()
242

    
243
            self.load_drawing_list()
244
            self.load_stylesheet_file()
245
            self.load_language_file()
246

    
247
            self.tabifyDockWidget(self.dockWidgetDrawingExplorer, self.dockWidgetSymbolExplorer)
248
            self.dockWidgetDrawingExplorer.raise_()
249

    
250
            self.read_settings()
251

    
252
            # ToDo..
253
            # Menu bar - Admin Hide
254
            # Loop Tab Hide
255
        except Exception as ex:
256
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
257
                      f"{sys.exc_info()[-1].tb_lineno}"
258
            self.addMessage.emit(MessageType.Error, message)
259

    
260
    def read_settings(self):
261
        """read geometry and state"""
262
        from App import App
263

    
264
        try:
265
            self.settings = QSettings(App.COMPANY, App.NAME)
266
            self.restoreGeometry(self.settings.value("geometry", ""))
267
            self.restoreState(self.settings.value("windowState", ""))
268
        except Exception as ex:
269
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
270
                                                           sys.exc_info()[-1].tb_lineno)
271
            self.addMessage.emit(MessageType.Error, message)
272

    
273
    def load_stylesheet_file(self):
274
        # load stylesheet file list
275
        stylesheet_name = QtWidgets.qApp.stylesheet_name
276
        files = [os.path.splitext(file)[0] for file in os.listdir(os.path.dirname(os.path.realpath(__file__))) if
277
                 os.path.splitext(file)[1] == '.qss']
278
        for file in files:
279
            action = self.menuTheme.addAction(file)
280
            action.setCheckable(True)
281
            action.setChecked(True) if stylesheet_name == file else action.setChecked(False)
282
            action.triggered.connect(partial(self.load_stylesheet, file))
283
        # up to here
284

    
285
    def load_language_file(self):
286
        # load language files
287
        language_name = QtWidgets.qApp.language_name
288
        files = ['en_us']  # englisgh is default language
289
        files.extend([os.path.splitext(file)[0] for file in
290
                      os.listdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'translate')) if
291
                      os.path.splitext(file)[1] == '.qm'])
292
        for file in files:
293
            action = self.menuLanguage.addAction(file)
294
            action.setCheckable(True)
295
            action.setChecked(True) if language_name.lower() == file.lower() else action.setChecked(False)
296
            action.triggered.connect(partial(self.load_language, file))
297
        # up to here
298

    
299
    def setMainWindowTitle(self, drawingName=None):
300
        try:
301
            _translate = QCoreApplication.translate
302

    
303
            version = QCoreApplication.applicationVersion()
304
            if drawingName is None:
305
                self.setWindowTitle(_translate(App.NAME + f"({version}) Licensed", App.NAME + f"({version}) Licensed"))
306
            else:
307
                self.setWindowTitle(
308
                    _translate(App.NAME + f"({version}) Licensed", App.NAME + f"({version}) Licensed") + f" - {drawingName}")
309
        except Exception as ex:
310
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
311
                      f"{sys.exc_info()[-1].tb_lineno}"
312
            self.addMessage.emit(MessageType.Error, message)
313

    
314
    def on_sync_stream_data(self):
315
        """
316
        sync stream data
317
        @return: None
318
        """
319

    
320
        drawing = AppDocData.instance().activeDrawing
321
        if drawing is None:
322
            return
323

    
324
        try:
325
            model = self.tableWidgetHMB.model()
326
            for col in range(2, model.columnCount()):
327
                hmb = None
328
                if self.tableWidgetHMB.isColumnHidden(col):
329
                    continue
330

    
331
                components_uid = model.item(1, col).text()
332
                hmb = drawing.hmbTable.get_hmb_data(components_uid)
333

    
334
                for row in range(2, 24):
335
                    item = model.item(row, col)
336
                    if Qt.magenta != item.background():
337
                        continue
338

    
339
                    value = item.text()
340
                    origin_value = item.data(Qt.UserRole)
341
                    """try to convert data type"""
342
                    value = float(value) if value and type(origin_value) is float else value
343

    
344
                    item.setBackground(Qt.white)
345
                    if row == 3:
346
                        hmb.phase_type = value
347
                    elif row == 4:  # Flowrate(Mass)
348
                        hmb.flowrate_mass = value
349
                        if value:
350
                            hmb.input_flowrate_type = 'Mass'
351
                    elif row == 5:  # Flowrate(Volume)
352
                        hmb.flowrate_volume = value
353
                        if value:
354
                            hmb.input_flowrate_type = 'Volume'
355
                    elif row == 6:
356
                        hmb.density = value
357
                    elif row == 7:
358
                        hmb.viscosity = value
359
                    elif row == 8:
360
                        hmb.temperature = value
361
                    elif row == 9:
362
                        hmb.molecular_weight = value
363
                    elif row == 10:
364
                        hmb.specific_heat_ratio = value
365
                    elif row == 11:
366
                        hmb.compress_factor = value
367
                    elif row == 12:
368
                        hmb.nominal_pipe_size = value
369
                    elif row == 13:
370
                        hmb.inside_pipe_size = value
371
                    elif row == 14:
372
                        hmb.schedule_no = value
373
                    elif row == 15:
374
                        hmb.straight_length = value
375
                    elif row == 16:
376
                        hmb.equivalent_length = value
377
                    elif row == 17:
378
                        hmb.equivalent_length_input = value
379
                    elif row == 18:
380
                        hmb.fitting_length = value
381
                    elif row == 19:
382
                        hmb.fitting_K = value
383
                    elif row == 20:
384
                        hmb.equivalent_length_cal = value
385
                    elif row == 21:
386
                        hmb.roughness = value
387
                    elif row == 22:
388
                        hmb.limitation_velocity = value
389
                    elif row == 23:
390
                        hmb.limitation_pressure_drop = value
391
        except Exception as ex:
392
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
393
                      f"{sys.exc_info()[-1].tb_lineno}"
394
            self.addMessage.emit(MessageType.Error, message)
395

    
396
    def clearlogs(self):
397
        """
398
        @brief: clear logs
399
        @return: None
400
        """
401
        try:
402
            self.listWidgetLogs.clear()
403
        except Exception as ex:
404
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
405
                      f'{sys.exc_info()[-1].tb_lineno}'
406
            self.addMessage.emit(MessageType.Error, message)
407
        finally:
408
            logs = self.listWidgetLogs.count()
409
            if logs:
410
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs({})'.format(logs))
411
            else:
412
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs')
413

    
414
    def closeEvent(self, event: QCloseEvent) -> None:
415
        """save geometry and state and then ask user to save drawing which is modified"""
416

    
417
        self.settings.setValue('geometry', self.saveGeometry())
418
        self.settings.setValue('windowState', self.saveState())
419

    
420
        self.save_drawing_if_necessary()
421

    
422
        """update view region"""
423
        app_doc_data = AppDocData.instance()
424
        if app_doc_data.activeDrawing:
425
            rect = self.graphicsView.viewport().rect()
426
            app_doc_data.activeDrawing.view_rect = self.graphicsView.mapToScene(rect).boundingRect()
427
            app_doc_data.update_view_region(app_doc_data.activeDrawing)
428
        """up to here"""
429

    
430
        event.accept()
431

    
432
    def open_context_menu(self, position):
433
        indexes = self.treeViewDrawingList.selectedIndexes()
434
        if len(indexes) > 0:
435
            index = indexes[0]
436
            item = index.model().itemFromIndex(index)
437

    
438
            menu = QMenu()
439

    
440
            if not item.parent():
441
                newDrawingAction = menu.addAction(QIcon(':/images/New.svg'), self.tr('New...'))
442
                newDrawingAction.triggered.connect(lambda: self.newDrawingActionClickEvent(item))
443
                menu.addAction(newDrawingAction)
444
            else:
445
                deleteDrawingAction = menu.addAction(QIcon(':/images/Cancel.svg'), self.tr("Delete"))
446
                deleteDrawingAction.triggered.connect(lambda: self.deleteDrawingActionClickEvent(item))
447
                menu.addAction(deleteDrawingAction)
448

    
449
            menu.exec_(self.treeViewDrawingList.viewport().mapToGlobal(position))
450

    
451
    def on_new_drawing(self):
452
        self.new_drawing()
453

    
454
    def newDrawingActionClickEvent(self, item):
455
        self.new_drawing()
456

    
457
    def saveAsDrawingActionClickEvent(self, item):
458
        sourceDb = item.text(2)
459

    
460
        self.saveAs_drawing(sourceDb)
461

    
462
    def deleteDrawingActionClickEvent(self, item):
463
        """ delete selected drawing """
464
        try:
465
            msg = QMessageBox(self)
466
            msg.setIcon(QMessageBox.Question)
467
            msg.setText(self.tr('Do you want to delete drawing on list?\nHowever, Actual drawing is not deleted.'))
468
            msg.setWindowTitle(self.tr("Delete"))
469
            msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
470

    
471
            if QMessageBox.Ok == msg.exec_():
472
                model = self.treeViewDrawingList.model()
473
                index = item.index()
474
                drawing_name = model.data(model.index(index.row(), 0, index.parent()))
475
                drawing_path = model.data(model.index(index.row(), 2, index.parent()))
476

    
477
                app_doc_data = AppDocData.instance()
478
                app_doc_data.deleteDrawingByName(drawing_path)
479

    
480
                if app_doc_data.activeDrawing:
481
                    if drawing_name == app_doc_data.activeDrawing.name and drawing_path == app_doc_data.activeDrawing.path:
482
                        app_doc_data.activeDrawing = None
483

    
484
                        if self.graphicsView.hasImage():
485
                            self.graphicsView.clearImage()
486
                            self.graphicsView.scene.clear()
487

    
488
                        self.initTableWidgetHMB()
489

    
490
                self.load_drawing_list()
491
        except Exception as ex:
492
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
493
                      f"{sys.exc_info()[-1].tb_lineno}"
494
            self.addMessage.emit(MessageType.Error, message)
495

    
496
    def clear_data(self):
497
        self.clear_HMB()
498

    
499
    def eventFilter(self, source, event):
500
        """ display mouse position of graphics view """
501
        if event.type() == QEvent.MouseMove:
502
            pos = self.graphicsView.mapToScene(event.pos())
503
            self._label_mouse.setText('mouse pos : ({},{})'.format(round(pos.x()), round(pos.y())))
504

    
505
        return QWidget.eventFilter(self, source, event)
506

    
507
    def load_stylesheet(self, file):
508
        """load stylesheets"""
509

    
510
        QtWidgets.qApp.loadStyleSheet(os.path.join(os.path.dirname(os.path.realpath(__file__)), file))
511

    
512
        app_doc_data = AppDocData.instance()
513
        configs = [Config('app', 'stylesheet', file)]
514
        app_doc_data.saveAppConfigs(configs)
515

    
516
        for action in self.menuTheme.actions():
517
            if action.text() == file: continue
518
            action.setChecked(False)
519

    
520
    def load_language(self, file):
521
        """ load language file and then apply selected language """
522
        try:
523

    
524
            qm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'translate', '{0}.qm'.format(file))
525
            QtWidgets.qApp.load_language(qm_file)
526

    
527
            app_doc_data = AppDocData.instance()
528
            configs = [Config('app', 'language', file)]
529
            app_doc_data.saveAppConfigs(configs)
530

    
531
            for action in self.menuLanguage.actions():
532
                if action.text().lower() == file.lower(): continue
533
                action.setChecked(False)
534
        finally:
535
            self.retranslateUi(self)
536

    
537
    def clear_output(self):
538
        self.tableWidgetOutput.setColumnCount(0)
539
        self.tableWidgetOutput.setRowCount(0)
540

    
541
        self.tableWidgetDeviation.setColumnCount(0)
542
        self.tableWidgetDeviation.setRowCount(0)
543

    
544
        app_doc_data = AppDocData.instance()
545
        app_doc_data.outputs.clear()
546

    
547
    def clear_loop(self):
548
        self.tableWidgetLoop.setColumnCount(0)
549

    
550
    def initTableWidgetHMB(self):
551
        app_doc_data = AppDocData.instance()
552

    
553
        column_infos = app_doc_data.getHMBDisplayNameAndUnitsExpression()
554
        """ convert unit """
555
        for row in range(column_infos.rowCount()):
556
            item = column_infos.item(row, 1)
557
            unit = self.convertToUnits(item.text())
558
            item.setText(unit)
559

    
560
        self.tableWidgetHMB.setModel(column_infos)
561

    
562
        self.tableWidgetHMB.hideRow(0)  # UID
563
        self.tableWidgetHMB.hideRow(1)  # Components_UID
564
        self.tableWidgetHMB.hideRow(2)  # Stream_No
565
        # self.tableWidgetHMB.hideRow(3)  # Phase_Type
566
        # self.tableWidgetHMB.hideRow(4)  # Flowrate_Mass
567
        # self.tableWidgetHMB.hideRow(5)  # Flowrate_Volume
568
        # self.tableWidgetHMB.hideRow(6)  # Density
569
        # self.tableWidgetHMB.hideRow(7)  # Viscosity
570
        # self.tableWidgetHMB.hideRow(8)  # Temperature
571
        # self.tableWidgetHMB.hideRow(9)  # Molecular_Weight
572
        # self.tableWidgetHMB.hideRow(10)  # Specific_Heat_Ratio
573
        # self.tableWidgetHMB.hideRow(11)  # Compress_Factor
574
        # self.tableWidgetHMB.hideRow(12)  # Nominal_Pipe_Size
575
        # self.tableWidgetHMB.hideRow(13)  # Inside_Pipe_Size
576
        # self.tableWidgetHMB.hideRow(14)  # Schedule_No
577
        # self.tableWidgetHMB.hideRow(15)  # Straight_Length
578
        # self.tableWidgetHMB.hideRow(16)  # Equivalent_Length
579
        self.tableWidgetHMB.hideRow(17)  # Equivalent_Length_Input
580
        self.tableWidgetHMB.hideRow(18)  # Fitting_Length
581
        self.tableWidgetHMB.hideRow(19)  # Fitting_K
582
        self.tableWidgetHMB.hideRow(20)  # Equivalent_Length_Cal
583
        # self.tableWidgetHMB.hideRow(21)  # Roughness
584
        # self.tableWidgetHMB.hideRow(22)  # Limitation_Velocity
585
        # self.tableWidgetHMB.hideRow(23)  # Limitation_Pressure_Drop
586
        # self.tableWidgetHMB.hideRow(24)  # Separator
587
        # self.tableWidgetHMB.hideRow(25)  # Velocity
588
        # self.tableWidgetHMB.hideRow(26)  # Reynolds
589
        # self.tableWidgetHMB.hideRow(27)  # Friction_Factor
590
        # self.tableWidgetHMB.hideRow(28)  # Pressure_Drop
591
        # self.tableWidgetHMB.hideRow(29)  # Pressure_Drop_Friction
592
        # self.tableWidgetHMB.hideRow(30)  # Pressure_Drop_Static
593
        # self.tableWidgetHMB.hideRow(31)  # Pressure_Pipe_End_Point
594
        self.tableWidgetHMB.hideRow(32)  # Power
595

    
596
        # self.tableWidgetHMB.setEditTriggers(QAbstractItemView.NoEditTriggers)
597
        self.tableWidgetHMB.verticalHeader().setVisible(False)
598

    
599
        col_names = ['Stream No.\n', 'Unit\n']
600
        column_infos.setHorizontalHeaderLabels(col_names)
601

    
602
        self.tableWidgetHMB.resizeColumnsToContents()
603
        self.tableWidgetHMB.resizeRowsToContents()
604

    
605
    def copy_selection(self, table_widget):
606
        """copy selected text to clipboard"""
607

    
608
        import io
609
        import csv
610

    
611
        selection = table_widget.selectedIndexes()
612
        if selection:
613
            rows = sorted(index.row() for index in selection)
614
            columns = sorted(index.column() for index in selection)
615
            rowcount = rows[-1] - rows[0] + 1
616
            colcount = columns[-1] - columns[0] + 1
617
            table = [[''] * colcount for _ in range(rowcount)]
618
            for index in selection:
619
                row = index.row() - rows[0]
620
                column = index.column() - columns[0]
621
                table[row][column] = index.data()
622
            stream = io.StringIO()
623
            csv.writer(stream, delimiter='\t').writerows(table)
624
            QApplication.clipboard().setText(stream.getvalue())
625

    
626
    def paste_selection(self, table_widget):
627
        """paste text of clipboard to table widget"""
628

    
629
        import io
630
        import csv
631

    
632
        selection = table_widget.selectedIndexes()
633
        if selection:
634
            model = table_widget.model()
635

    
636
            buffer = QApplication.clipboard().text()
637
            rows = sorted(index.row() for index in selection)
638
            columns = sorted(index.column() for index in selection)
639
            reader = csv.reader(io.StringIO(buffer), delimiter='\t')
640
            if len(rows) == 1 and len(columns) == 1:
641
                for i, line in enumerate(reader):
642
                    for j, cell in enumerate(line):
643
                        index = model.index(rows[0] + i, columns[0] + j)
644
                        item = model.item(index.row(), index.column())
645
                        if item.isEditable():
646
                            data = model.data(index, Qt.UserRole)
647
                            if type(data) is not type(cell) or data != cell:
648
                                item.setBackground(Qt.magenta)
649
                                model.setData(index, cell)
650
            else:
651
                arr = [[cell for cell in row] for row in reader]
652
                for index in selection:
653
                    row = index.row() - rows[0]
654
                    column = index.column() - columns[0]
655
                    index = model.index(index.row(), index.column())
656
                    item = model.item(index.row(), index.column())
657
                    if item.isEditable():
658
                        data = model.data(index, Qt.UserRole)
659
                        if type(data) is not type(arr[row][column]) or data != arr[row][column]:
660
                            item.setBackground(Qt.magenta)
661
                            model.setData(index, arr[row][column])
662

    
663
    '''
664
        @brief      Drawing 속성의 Units 에서 Key값을 이용하여 Value를 찾음
665
        @author     yeonjin
666
        @date       19.08.30
667
    '''
668

    
669
    def findValue(self, key):
670
        value = None
671

    
672
        key = key.replace("{", "").replace("}", "")
673
        for attr in AppDocData.instance().activeDrawing.attrs:
674
            if attr[0] == 'Units':
675
                if key in attr[1]:
676
                    value = attr[1][key]
677
                    break
678

    
679
        return value
680

    
681
    '''
682
        @brief      Convert UnitsExpression To Units Value
683
        @author     yeonjin
684
        @date       19.08.29
685
    '''
686

    
687
    def convertToUnits(self, unitsExpression):
688
        import re
689

    
690
        if unitsExpression is None or AppDocData.instance().activeDrawing is None:
691
            return ''
692

    
693
        found = re.findall('{.*?}', unitsExpression)
694
        for f in found:
695
            key = f
696
            val = self.findValue(key)
697
            if val:
698
                unitsExpression = unitsExpression.replace(key, val)
699

    
700
        return unitsExpression
701

    
702
    def on_left_alignment(self):
703
        """align selected items to left"""
704

    
705
        try:
706
            app_doc_data = AppDocData.instance()
707
            if app_doc_data._activeDrawing is not None:
708
                items = self.graphicsView.scene.selectedItems()
709
                if items:
710
                    self.graphicsView.scene._undo_stack.push(
711
                        LeftAlignmentCommand.LeftAlignmentCommand(self.graphicsView.scene,
712
                                                                  self.graphicsView.scene.selectedItems()))
713
        except Exception as ex:
714
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
715
                      f"{sys.exc_info()[-1].tb_lineno}"
716
            self.addMessage.emit(MessageType.Error, message)
717

    
718
    def on_right_alignment(self):
719
        """align selected items to right"""
720

    
721
        try:
722
            app_doc_data = AppDocData.instance()
723
            if app_doc_data._activeDrawing is not None:
724
                items = self.graphicsView.scene.selectedItems()
725
                if items:
726
                    self.graphicsView.scene._undo_stack.push(
727
                        RightAlignmentCommand.RightAlignmentCommand(self.graphicsView.scene,
728
                                                                    self.graphicsView.scene.selectedItems()))
729
        except Exception as ex:
730
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
731
                      f"{sys.exc_info()[-1].tb_lineno}"
732
            self.addMessage.emit(MessageType.Error, message)
733

    
734
    def on_up_alignment(self):
735
        """align selected items to up"""
736

    
737
        try:
738
            app_doc_data = AppDocData.instance()
739
            if app_doc_data._activeDrawing is not None:
740
                items = self.graphicsView.scene.selectedItems()
741
                if items:
742
                    self.graphicsView.scene._undo_stack.push(
743
                        UpAlignmentCommand.UpAlignmentCommand(self.graphicsView.scene,
744
                                                              self.graphicsView.scene.selectedItems()))
745
        except Exception as ex:
746
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
747
                      f"{sys.exc_info()[-1].tb_lineno}"
748
            self.addMessage.emit(MessageType.Error, message)
749

    
750
    def on_down_alignment(self):
751
        """align selected items to down"""
752

    
753
        try:
754
            app_doc_data = AppDocData.instance()
755
            if app_doc_data._activeDrawing is not None:
756
                items = self.graphicsView.scene.selectedItems()
757
                if items:
758
                    self.graphicsView.scene._undo_stack.push(
759
                        DownAlignmentCommand.DownAlignmentCommand(self.graphicsView.scene,
760
                                                                  self.graphicsView.scene.selectedItems()))
761
        except Exception as ex:
762
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
763
                      f"{sys.exc_info()[-1].tb_lineno}"
764
            self.addMessage.emit(MessageType.Error, message)
765

    
766
    def load_drawing_list(self):
767
        """load pfd drawing list"""
768

    
769
        try:
770
            app_doc_data = AppDocData.instance()
771
            drawings = app_doc_data.getDrawings()
772

    
773
            if self.treeViewDrawingList.model():
774
                self.treeViewDrawingList.model().clear()
775

    
776
            model = QStandardItemModel()
777
            root = [QStandardItem(self.tr('Drawings')), QStandardItem('')]
778
            model.appendRow(root)
779

    
780
            for drawing in drawings:
781
                items = [QStandardItem(drawing.name), QStandardItem(drawing.date_time), QStandardItem(drawing.path)]
782
                items[0].setIcon(QIcon(':images/PFD.png'))
783
                items[0].setData(drawing, Qt.UserRole)
784
                root[0].appendRow(items)
785

    
786
            model.setHorizontalHeaderLabels([self.tr('Name'), self.tr('DateTime'), self.tr('Path')])
787
            self.treeViewDrawingList.setModel(model)
788

    
789
            root[0].setText(self.tr('Drawings') + f"({root[0].rowCount()})")
790
            self.treeViewDrawingList.expandAll()
791
            self.treeViewDrawingList.sortByColumn(1, Qt.DescendingOrder)
792
            self.treeViewDrawingList.resizeColumnToContents(0)
793
            self.treeViewDrawingList.hideColumn(2)
794
        except Exception as ex:
795
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
796
                      f"{sys.exc_info()[-1].tb_lineno}"
797
            self.addMessage.emit(MessageType.Error, message)
798

    
799
    def save_drawing_if_necessary(self):
800
        """ask user to save drawing or not when drawing is modified"""
801

    
802
        app_doc_data = AppDocData.instance()
803
        if app_doc_data.activeDrawing and app_doc_data.activeDrawing.modified:
804
            if QMessageBox.Yes == QMessageBox.question(self, self.tr("Question"),
805
                                                       self.tr("Do you want to save drawing?"),
806
                                                       QMessageBox.Yes | QMessageBox.No):
807
                self.actionSaveCliked(False)
808

    
809
    def open_selected_drawing(self, index: QModelIndex):
810
        """ open selected drawing """
811
        try:
812
            model = self.treeViewDrawingList.model()
813
            root = index.parent()
814
            if root:
815
                file_path = model.data(model.index(index.row(), 2, root), Qt.DisplayRole)
816
                if os.path.exists(file_path):
817
                    self.save_drawing_if_necessary()
818
                    self.open_drawing(file_path)
819
        except Exception as ex:
820
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
821
                      f"{sys.exc_info()[-1].tb_lineno}"
822
            self.addMessage.emit(MessageType.Error, message)
823

    
824
    def actionSaveCliked(self, show_message=True):
825
        """action save click event"""
826
        from datetime import datetime
827
        from AppDocData import AppDocData
828
        from EngineeringAbstractItem import QEngineeringAbstractItem
829

    
830
        try:
831
            app_doc_data = AppDocData.instance()
832
            if app_doc_data.activeDrawing is None:
833
                self.showImageSelectionMessageBox()
834
                return
835

    
836
            app_doc_data.activeDrawing.clearItemList()
837

    
838
            items = self.graphicsView.scene.items()
839
            for item in items:
840
                if issubclass(type(item), QEngineeringAbstractItem):
841
                    app_doc_data.activeDrawing.allItems.append(item)
842

    
843
            count = len(app_doc_data.activeDrawing.allItems)
844
            if count > 0:
845
                try:
846
                    self.progress = QProgressDialog(self.tr("Please wait for a while"), self.tr("Cancel"), 0, 100,
847
                                                    self) if not hasattr(self, 'progress') else self.progress
848
                    self.progress.setWindowModality(Qt.WindowModal)
849
                    self.progress.setAutoReset(True)
850
                    self.progress.setAutoClose(True)
851
                    self.progress.setMinimum(0)
852
                    self.progress.resize(600, 100)
853
                    self.progress.setWindowTitle(self.tr("Save"))
854
                    self.progress.show()
855

    
856
                    self.save_drawing_data()
857
                finally:
858
                    self.load_drawing_list()
859
                    self.progress.setValue(self.progress.maximum())
860
                    self.progress.hide()
861
                    if show_message:
862
                        QMessageBox.information(self, self.tr("Information"), self.tr("Save completed successfully."))
863

    
864
        except Exception as ex:
865
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
866
                      f"{sys.exc_info()[-1].tb_lineno}"
867
            self.addMessage.emit(MessageType.Error, message)
868

    
869
    def on_save(self):
870
        self.actionSaveCliked()
871

    
872
    def on_save_as(self):
873
        """save as drawing file"""
874
        from shutil import copyfile
875
        from datetime import datetime
876
        from Drawing import Drawing
877

    
878
        app_doc_data = AppDocData.instance()
879
        workspace = self.get_work_space()
880

    
881
        file_name = os.path.join(workspace, 'copy of ' + app_doc_data.activeDrawing.name + '.hytos')
882
        options = QFileDialog.Options()
883
        options |= QFileDialog.DontUseNativeDialog
884
        name, _ = QFileDialog.getSaveFileName(self, self.tr('Save As'), file_name, 'HYTOS(*.hytos)', options=options)
885
        if name:
886
            try:
887
                if os.path.splitext(name)[1] != '.hytos': name += '.hytos'
888

    
889
                self.actionSaveCliked()  # save current drawing
890

    
891
                app_doc_data = AppDocData.instance()
892
                # copy current drawing file to new drawing file
893
                copyfile(app_doc_data.activeDrawing.path, name)
894
                app_doc_data.activeDrawing.path = name
895

    
896
                matches = [drawing for drawing in app_doc_data.getDrawings()
897
                           if os.path.exists(drawing.path) and os.path.samefile(drawing.path, name)]
898
                if not matches:
899
                    drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
900
                    app_doc_data.saveDrawing(drawing)
901
                else:
902
                    drawing = Drawing(str(matches[0].UID), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
903
                    app_doc_data.updateDrawing(drawing)
904

    
905
                self.load_drawing_list()
906
                self.open_border_file()
907
                self.load_data(drawing)
908

    
909
                app_doc_data.activeDrawing.modified = False
910
                self.setMainWindowTitle(f"{app_doc_data.activeDrawing.path}")
911
            except Exception as ex:
912
                message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
913
                                                               sys.exc_info()[-1].tb_lineno)
914
                self.addMessage.emit(MessageType.Error, message)
915

    
916
    def on_undo(self):
917
        """undo"""
918
        self.graphicsView.scene.undo_stack.undo()
919

    
920
    def on_redo(self):
921
        """redo"""
922
        self.graphicsView.scene.undo_stack.redo()
923

    
924
    def save_drawing_data(self):
925
        """ save drawing data """
926

    
927
        from datetime import datetime
928
        from AppDocData import AppDocData
929
        from SymbolSvgItem import SymbolSvgItem
930
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
931
        from EngineeringCalloutTextItem import QEngineeringCalloutTextItem
932
        from EngineeringCloudItem import QEngineeringCloudItem
933
        from EngineeringDimensionItem import QEngineeringDimensionItem
934

    
935
        try:
936
            app_doc_data = AppDocData.instance()
937
            items = app_doc_data.activeDrawing.allItems
938

    
939
            maxValue = len(items)
940
            self.progress.setMaximum(maxValue)
941

    
942
            app_doc_data.saveToDatabase([item for item in self.graphicsView.scene.items()
943
                                         if hasattr(item, 'toSql') and (
944
                                                 type(item) is SymbolSvgItem or
945
                                                 type(item) is QEngineeringStreamlineItem or
946
                                                 type(item) is QEngineeringCalloutTextItem or
947
                                                 type(item) is QEngineeringDimensionItem or
948
                                                 type(item) is QEngineeringCloudItem)],
949
                                        self.progress.setValue)
950

    
951
            if app_doc_data.activeDrawing:
952
                app_doc_data.activeDrawing.hmbTable.saveData()
953
                app_doc_data.save_sheet_history('Save')
954

    
955
            """update drawing's modified time"""
956
            drawings = app_doc_data.getDrawings()
957
            drawing = [drawing for drawing in drawings if app_doc_data.activeDrawing.UID == drawing.UID]
958
            if drawing:
959
                drawing[0].date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
960
                rect = self.graphicsView.viewport().rect()
961
                drawing[0].view_rect = self.graphicsView.mapToScene(rect).boundingRect()
962
                app_doc_data.updateDrawing(drawing[0])
963

    
964
            app_doc_data.activeDrawing.modified = False
965
            self.setMainWindowTitle(f"{app_doc_data.activeDrawing.path}")
966

    
967
        except Exception as ex:
968
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
969
                      f"{sys.exc_info()[-1].tb_lineno}"
970
            self.addMessage.emit(MessageType.Error, message)
971

    
972
    '''
973
        @brief  add message listwidget
974
        @author humkyung
975
        @date   2018.07.31
976
    '''
977

    
978
    def onAddMessage(self, messageType, message):
979
        from AppDocData import MessageType
980

    
981
        try:
982
            current = QDateTime.currentDateTime()
983

    
984
            item = QListWidgetItem('{}: {}'.format(current.toString('hh:mm:ss'), message))
985
            if messageType == MessageType.Error:
986
                item.setForeground(Qt.red)
987
            elif messageType == MessageType.Information:
988
                item.setForeground(Qt.blue)
989

    
990
            self.listWidgetLogs.insertItem(0, item)
991
        except Exception as ex:
992
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
993
                                                       sys.exc_info()[-1].tb_lineno))
994
        finally:
995
            logs = self.listWidgetLogs.count()
996
            if logs:
997
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs({})'.format(logs))
998
            else:
999
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs')
1000

    
1001
    '''
1002
        @brief      Area Zoom
1003
        @author     Jeongwoo
1004
        @date       2018.06.27
1005
        @history    connect command's rejected signal
1006
    '''
1007

    
1008
    def onAreaZoom(self, action):
1009
        if self.actionZoom.isChecked():
1010
            cmd = AreaZoomCommand.AreaZoomCommand(self.graphicsView)
1011
            cmd.onRejected.connect(self.onCommandRejected)
1012
            self.graphicsView.command = cmd
1013

    
1014
    def fitWindow(self, action):
1015
        """fit window"""
1016
        app_doc_data = AppDocData.instance()
1017
        if app_doc_data.activeDrawing:
1018
            self.graphicsView.useDefaultCommand()
1019
            scene_rect = self.graphicsView.scene.itemsBoundingRect()
1020
            if scene_rect.left() > 0:
1021
                scene_rect.setLeft(0)
1022
            if scene_rect.top() > 0:
1023
                scene_rect.setTop(0)
1024
            if scene_rect.right() < 1189:
1025
                scene_rect.setRight(1189)
1026
            if scene_rect.bottom() < 841:
1027
                scene_rect.setBottom(841)
1028

    
1029
            self.graphicsView.setSceneRect(scene_rect)
1030
            if action is None and app_doc_data.activeDrawing.view_rect.isValid():
1031
                self.graphicsView.zoom_rect(app_doc_data.activeDrawing.view_rect)
1032
            else:
1033
                self.graphicsView.zoomImageInit()
1034
            self.graphicsView.scene.invalidate()
1035

    
1036
    def on_view_connector(self, checked):
1037
        """turn on/off connector"""
1038
        for item in self.graphicsView.scene.items():
1039
            if not hasattr(item, 'connectors'):
1040
                continue
1041
            for connector in item.connectors:
1042
                connector.setVisible(True) if checked else connector.hide()
1043

    
1044
    def scene_changed(self):
1045
        """update modified flag"""
1046

    
1047
        app_doc_data = AppDocData.instance()
1048
        app_doc_data.activeDrawing.modified = True
1049
        self.setMainWindowTitle(f"{app_doc_data.activeDrawing.path}*")
1050

    
1051
    def onSelectionChanged(self):
1052
        """selection changed"""
1053
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
1054

    
1055
        items = [item for item in self.graphicsView.scene.selectedItems() if
1056
                 issubclass(type(item), SymbolSvgItem) or type(item) is QEngineeringStreamlineItem]
1057
        if items:
1058
            item = items[-1]
1059

    
1060
            if type(item) is QEngineeringErrorItem:
1061
                for index in range(self.tableWidgetHMB.rowCount()):
1062

    
1063
                    if self.tableWidgetHMB.item(index, 1).tag is item:
1064
                        self.tableWidgetHMB.selectRow(index)
1065
                        break
1066

    
1067
    '''
1068
        @brief      Initialize scene and itemTreeWidget
1069
        @author     Jeongwoo
1070

1071
        @date       2018.06.14
1072
        @history    humkyung 2018.08.16 ask to delete recognized items before remove
1073
    '''
1074

    
1075
    def onInitializeScene(self, action):
1076
        try:
1077
            msg = QMessageBox(self)
1078
            msg.setIcon(QMessageBox.Question)
1079
            msg.setText(self.tr('Do you want to remove all items?\nThis work cannot be recovered.'))
1080
            msg.setWindowTitle(self.tr("Clear Screen"))
1081
            msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
1082
            if QMessageBox.Ok == msg.exec_():
1083

    
1084
                appDocData = AppDocData.instance()
1085
                appDocData.activeDrawing.hmbTable.reset()
1086
                appDocData.clearItemList(True)
1087

    
1088
                items = self.graphicsView.scene.items()
1089
                for item in items:
1090
                    if type(item) is not QGraphicsPixmapItem and item.scene() is not None:
1091
                        self.graphicsView.scene.removeItem(item)
1092

    
1093
                appDocData.initializeDataByDrawingUID(appDocData.activeDrawing.UID)
1094

    
1095
                self.initTableWidgetHMB()
1096

    
1097
        except Exception as ex:
1098
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1099
                                                           sys.exc_info()[-1].tb_lineno)
1100
            self.addMessage.emit(MessageType.Error, message)
1101

    
1102
    '''
1103
        @brief      Manage Checkable Action statement
1104
        @author     Jeongwoo
1105
        @date       2018.05.10
1106
        @history    2018.06.27  Jeongwoo    Chnage method to initialize command [Set None → DefaultCommand]
1107
    '''
1108

    
1109
    def actionGroupTriggered(self, action):
1110
        if hasattr(self.actionLine, 'tag'):
1111
            self.actionLine.tag.onRejected.emit(None)
1112

    
1113
        if self.graphicsView.command is not None:
1114
            self.graphicsView.useDefaultCommand()
1115

    
1116
        for _action in [x for x in self.actionGroup.actions() if x is not action]:
1117
            _action.setChecked(False)
1118
            if _action is self.actionLine:
1119
                for item in self.graphicsView.scene.items():
1120
                    if not hasattr(item, 'connectors'): continue
1121
                    for connector in item.connectors: connector.hide()
1122

    
1123
        action.setChecked(True)
1124
        if action is self.actionLine:
1125
            for item in self.graphicsView.scene.items():
1126
                if not hasattr(item, 'connectors'): continue
1127
                for connector in item.connectors: connector.setVisible(True)
1128

    
1129
    def showSymbolEditor(self):
1130
        from SymbolEditorDialog import QSymbolEditorDialog
1131

    
1132
        dlg = QSymbolEditorDialog()
1133
        dlg.exec_()
1134

    
1135
    def editor_lost_focus(self, item):
1136
        cursor = item.textCursor()
1137
        cursor.clearSelection()
1138
        item.setTextCursor(cursor)
1139

    
1140
        """
1141
        if item.toPlainText():
1142
            self.removeItem(item)
1143
            item.deleteLater()
1144
        """
1145

    
1146
    def item_selected(self, item):
1147
        pass
1148
        """
1149
        font = item.font()
1150
        color = item.defaultTextColor()
1151
        self.fontCombo.setCurrentFont(font)
1152
        self.fontSizeCombo.setEditText(str(font.pointSize()))
1153
        self.boldAction.setChecked(font.weight() == QFont.Bold)
1154
        self.italicAction.setChecked(font.italic())
1155
        self.underlineAction.setChecked(font.underline())
1156
        """
1157

    
1158
    def on_callout_created(self):
1159
        from CreateCommand import CreateCommand
1160
        try:
1161
            QApplication.restoreOverrideCursor()
1162

    
1163
            callout = self.actionPlaceTextBox.tag.callout
1164
            callout.setTextInteractionFlags(Qt.TextEditorInteraction)
1165
            callout.setFocus()
1166
            callout.lost_focus.connect(self.editor_lost_focus)
1167
            callout.selected_change.connect(self.item_selected)
1168

    
1169
            self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [callout, ]))
1170
        finally:
1171
            self.actionPlaceTextBox.tag.reset()
1172

    
1173
    def on_place_callout_textbox(self):
1174
        """place callout textbox"""
1175
        from PlaceCalloutCommand import PlaceCalloutCommand
1176

    
1177
        if not hasattr(self.actionPlaceTextBox, 'tag'):
1178
            self.actionPlaceTextBox.tag = PlaceCalloutCommand(self.graphicsView)
1179

    
1180
        self.actionPlaceTextBox.tag.onSuccess.connect(self.on_callout_created)
1181
        self.actionPlaceTextBox.tag.onRejected.connect(self.onCommandRejected)
1182

    
1183
        self.graphicsView.command = self.actionPlaceTextBox.tag
1184

    
1185
    def on_dimension_created(self):
1186
        from CreateCommand import CreateCommand
1187
        try:
1188
            QApplication.restoreOverrideCursor()
1189

    
1190
            dimension = self.actionPlaceDimension.tag.dimension
1191
            dimension.transfer.onRemoved.connect(self.on_item_removed)
1192

    
1193
            self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [dimension, ]))
1194
        finally:
1195
            self.actionPlaceDimension.tag.reset()
1196

    
1197
    def on_place_dimension(self):
1198
        """place dimension"""
1199
        from PlaceDimensionCommand import PlaceDimensionCommand
1200

    
1201
        if not hasattr(self.actionPlaceDimension, 'tag'):
1202
            self.actionPlaceDimension.tag = PlaceDimensionCommand(self.graphicsView)
1203

    
1204
        self.actionPlaceDimension.tag.onSuccess.connect(self.on_dimension_created)
1205
        self.actionPlaceDimension.tag.onRejected.connect(self.onCommandRejected)
1206

    
1207
        self.graphicsView.command = self.actionPlaceDimension.tag
1208

    
1209
    def on_place_cloud(self):
1210
        """place a cloud"""
1211
        from PlaceCloudCommand import PlaceCloudCommand
1212

    
1213
        def on_cloud_created():
1214
            from CreateCommand import CreateCommand
1215
            try:
1216
                QApplication.restoreOverrideCursor()
1217

    
1218
                cloud = self.actionPlaceCloud.tag.cloud
1219
                self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [cloud, ]))
1220
            finally:
1221
                self.actionPlaceCloud.tag.reset()
1222

    
1223
        if not hasattr(self.actionPlaceCloud, 'tag'):
1224
            self.actionPlaceCloud.tag = PlaceCloudCommand(self.graphicsView)
1225

    
1226
        self.actionPlaceCloud.tag.onSuccess.connect(on_cloud_created)
1227
        self.actionPlaceCloud.tag.onRejected.connect(self.onCommandRejected)
1228

    
1229
        self.graphicsView.command = self.actionPlaceCloud.tag
1230

    
1231
    def on_help(self):
1232
        """open user manual"""
1233
        import os
1234

    
1235
        try:
1236
            help_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'HYTOS User Manual.pdf')
1237
            os.startfile(f"\"{help_file_path}\"")
1238
            # os.system(f"\"{help_file_path}\"")
1239
        except Exception as ex:
1240
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1241
                                                           sys.exc_info()[-1].tb_lineno)
1242
            self.addMessage.emit(MessageType.Error, message)
1243

    
1244
    def on_about(self):
1245
        """show about dialog"""
1246
        from AboutDialog import QAboutDialog
1247

    
1248
        dlg = QAboutDialog()
1249
        dlg.exec_()
1250

    
1251
    def on_readme(self):
1252
        """open readme.html"""
1253

    
1254
        file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'readme.html')
1255
        os.startfile(f"\"{file_path}\"")
1256

    
1257
    def update_label_contents(self):
1258
        from EngineeringCalloutTextItem import QEngineeringCalloutTextItem
1259

    
1260
        items = [item for item in self.graphicsView.scene.items() if issubclass(type(item), SymbolSvgItem) or
1261
                 type(item) is QEngineeringStreamlineItem or type(item) is QEngineeringCalloutTextItem]
1262
        for item in items:
1263
            item.update_label_contents()
1264

    
1265
    def onOptions(self):
1266
        from OptionsDialog import QOptionsDialog
1267
        try:
1268
            app_doc_data = AppDocData.instance()
1269
            configs = app_doc_data.getAppConfigs('option', 'TagFontSize')
1270
            old_tag_font_size = configs[0].value if configs and len(configs) == 1 else 6
1271

    
1272
            configs = app_doc_data.getAppConfigs('option', 'TagFontColor')
1273
            old_tag_font_color = configs[0].value if configs and len(configs) == 1 else '#000000'
1274

    
1275
            configs = app_doc_data.getAppConfigs('option', 'CalloutFontSize')
1276
            old_callout_font_size = configs[0].value if configs and len(configs) == 1 else 6
1277

    
1278
            configs = app_doc_data.getAppConfigs('option', 'CalloutTextColor')
1279
            old_callout_text_color = configs[0].value if configs and len(configs) == 1 else '#000000'
1280

    
1281
            dlg = QOptionsDialog(self)
1282
            dlg.setWindowFlags(dlg.windowFlags() & ~Qt.WindowContextHelpButtonHint)
1283
            if QDialog.Accepted == dlg.exec_():
1284
                if app_doc_data.activeDrawing:
1285
                    if str(old_tag_font_size) != str(dlg.tag_font_size) or \
1286
                            old_tag_font_color != dlg.tag_text_color or \
1287
                            str(old_callout_font_size) != str(dlg.callout_font_size) or \
1288
                            old_callout_text_color != dlg.callout_text_color:
1289
                        self.update_label_contents()
1290

    
1291
        except Exception as ex:
1292
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1293
                                                           sys.exc_info()[-1].tb_lineno)
1294
            self.addMessage.emit(MessageType.Error, message)
1295

    
1296
    def change_output_font_color(self):
1297
        try:
1298
            row_count = self.tableWidgetDeviation.rowCount()
1299
            col_count = self.tableWidgetDeviation.columnCount()
1300
            for row in range(row_count):
1301
                for col in range(col_count):
1302
                    if row == 0:
1303
                        item = self.tableWidgetDeviation.item(row, col)
1304
                        item.setText('RUN CALCULATION')
1305
                        item.setForeground(Qt.red)
1306
                        self.tableWidgetDeviation.setItem(row, col, item)
1307
                    else:
1308
                        self.tableWidgetDeviation.item(row, col).setForeground(QBrush(QColor(169, 169, 169)))
1309

    
1310
            row_count = self.tableWidgetOutput.rowCount()
1311
            col_count = self.tableWidgetOutput.columnCount()
1312
            for row in range(row_count):
1313
                for col in range(col_count):
1314
                    self.tableWidgetOutput.item(row, col).setForeground(QBrush(QColor(169, 169, 169)))
1315
                    col_span = self.tableWidgetOutput.columnSpan(row, col)
1316
                    if col_span > 1:
1317
                        break
1318
        except Exception as ex:
1319
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1320
                                                           sys.exc_info()[-1].tb_lineno)
1321
            self.addMessage.emit(MessageType.Error, message)
1322

    
1323
    def calculation(self, saving=False):
1324
        """execute hydro calculation"""
1325
        from AppDocData import AppDocData
1326
        from Calculation import Calculation
1327
        from HydroCalculationCommand import HydroCalculationCommand
1328
        from CalculationValidation import QCalculationValidation
1329
        from ValidationDialog import QValidationDialog
1330

    
1331
        try:
1332
            app_doc_data = AppDocData.instance()
1333
            if app_doc_data.activeDrawing is None:
1334
                self.showImageSelectionMessageBox()
1335
                return
1336

    
1337
            hmbs = app_doc_data.activeDrawing.hmbTable._hmbs
1338
            if hmbs is not None:
1339
                try:
1340
                    errors = []
1341
                    items = [item for item in self.graphicsView.scene.items() if type(item) is SymbolSvgItem or
1342
                             type(item) is QEngineeringStreamlineItem]
1343
                    for item in items:
1344
                        error = item.validate()
1345
                        if error:
1346
                            errors.extend(error)
1347

    
1348
                    HydroCalculationCommand.ERRORS = 0
1349
                    if errors:
1350
                        dlg = QValidationDialog(self, errors)
1351
                        dlg.show()
1352
                        HydroCalculationCommand.ERRORS = len(errors)
1353
                    else:
1354
                        self.progress = QProgressDialog(self.tr("Please wait for a while"), self.tr("Cancel"), 0, 100, self) \
1355
                            if not hasattr(self, 'progress') else self.progress
1356
                        self.progress.setWindowModality(Qt.WindowModal)
1357
                        self.progress.setAutoReset(True)
1358
                        self.progress.setAutoClose(True)
1359
                        self.progress.setMinimum(0)
1360
                        self.progress.resize(600, 100)
1361
                        self.progress.setWindowTitle(self.tr("Calculate data..."))
1362
                        self.progress.show()
1363

    
1364
                        maxValue = len(hmbs)
1365
                        self.progress.setMaximum(maxValue)
1366

    
1367
                        for hmb in hmbs:
1368
                            self.progress.setValue(self.progress.value() + 1)
1369

    
1370
                            if hmb.phase_type:
1371
                                Calculation(hmb)
1372

    
1373
                            QApplication.processEvents()
1374

    
1375
                        messages = None
1376
                        """ generate loop """
1377
                        cmd = HydroCalculationCommand(self.graphicsView)
1378
                        messages = cmd.execute(None)
1379
                        cmd.execute_second(None)
1380

    
1381
                        app_doc_data.activeDrawing.loops = cmd.loops
1382

    
1383
                        self.display_loops()
1384
                        self.display_output()
1385

    
1386
                        self.load_HMB()
1387

    
1388
                        """pop up error message dialog if there are some errors"""
1389
                        if messages:
1390
                            dlg = QCalculationValidation()
1391
                            dlg.show_dialog('Calculation will be terminated!', messages)
1392

    
1393
                        if saving:
1394
                            app_doc_data.save_sheet_history('Calculation')
1395
                finally:
1396
                    self.progress.setValue(self.progress.maximum())
1397
                    self.progress.hide()
1398
        except Exception as ex:
1399
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1400
                      f"{sys.exc_info()[-1].tb_lineno}"
1401
            self.addMessage.emit(MessageType.Error, message)
1402

    
1403
    def on_generate_report(self):
1404
        """generate calculation report"""
1405
        from tempfile import NamedTemporaryFile
1406
        import openpyxl
1407
        from DrawImage import DrawImage
1408
        from openpyxl.styles import Font
1409
        from datetime import datetime
1410
        # from EngineeringStreamlineItem import QEngineeringStreamlineItem
1411

    
1412
        try:
1413
            app_doc_data = AppDocData.instance()
1414
            if app_doc_data.activeDrawing is None:
1415
                self.showImageSelectionMessageBox()
1416
                return
1417

    
1418
            image_path = None
1419
            with NamedTemporaryFile() as f:
1420
                image_path = f.name + '.png'
1421

    
1422
            if image_path:
1423
                self.on_view_connector(False)  # hide connector
1424
                self.graphicsView.save_as_image(image_path)
1425

    
1426
                workspace = self.get_work_space()
1427
                file_name = os.path.join(workspace, app_doc_data.activeDrawing.name + '.xlsx')
1428
                options = QFileDialog.Options()
1429
                options |= QFileDialog.DontUseNativeDialog
1430
                file_name, _ = QFileDialog.getSaveFileName(self, self.tr('Report'), file_name, 'Excel(*.xlsx)',
1431
                                                           options=options)
1432

    
1433
                template = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Report_Template.xlsx')
1434

    
1435
                # get image file path
1436
                company_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Company.png')
1437
                horizontal_flow_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Horizontal_Flow.png')
1438
                vertical_upward_flow_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME,
1439
                                                               'Vertical_Upward_Flow.png')
1440
                vertical_downward_flow_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME,
1441
                                                                 'Vertical_Downward_Flow.png')
1442
                # up to here
1443
                if file_name and os.path.exists(template):
1444
                    units = [attr[1] for attr in app_doc_data.activeDrawing.attrs if attr[0] == 'Units'][0]
1445

    
1446
                    wb = openpyxl.load_workbook(template)
1447

    
1448
                    page_no = 1
1449
                    pages = math.ceil(len(list(app_doc_data.activeDrawing.hmbTable.streamNos())) / 20)
1450
                    for page in range(pages):
1451
                        ws = wb.copy_worksheet(wb.get_sheet_by_name('Page'))
1452
                        ws.title = f"Page ({page_no})"
1453

    
1454
                        cal_image = openpyxl.drawing.image.Image(image_path)
1455
                        # rect = self.graphicsView.sceneRect()
1456
                        x_scale = MainWindow.DEFAULT_SIZE.width() / cal_image.width
1457
                        y_scale = MainWindow.DEFAULT_SIZE.height() / cal_image.height
1458
                        scale = min(x_scale, y_scale)
1459
                        cal_image.width *= scale
1460
                        cal_image.height *= scale
1461
                        ws.add_image(cal_image, 'C4')
1462

    
1463
                        company_image = openpyxl.drawing.image.Image(company_image_path)
1464
                        company_image.width *= 0.8
1465
                        company_image.height *= 0.7
1466
                        ws.add_image(company_image, 'X54')
1467

    
1468
                        """write sheet information"""
1469
                        configs = app_doc_data.getConfigs('Sheet')
1470
                        for config in configs:
1471
                            if config.key == 'JobNo' and config.value:
1472
                                ws['Z59'].value = config.value
1473
                            elif config.key == 'JobName' and config.value:
1474
                                ws['W60'].value = config.value
1475
                            elif config.key == 'Description' and config.value:
1476
                                ws['W74'].value = config.value
1477
                            elif config.key == 'RevStatus' and config.value:
1478
                                ws['Y69'].value = config.value
1479
                            elif config.key == 'CheckedBy' and config.value:
1480
                                ws['Z79'].value = config.value
1481
                            elif config.key == 'ApprovedBy' and config.value:
1482
                                ws['Z80'].value = config.value
1483

    
1484
                        configs = app_doc_data.getAppConfigs('app', 'SingleID')
1485
                        if configs and configs[0].value:
1486
                            ws['Z78'].value = configs[0].value
1487
                        ws['Z69'].value = str(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
1488
                        """up to here"""
1489

    
1490
                        # Output [Pump, Compressor, Control Valve]
1491
                        ws['X1'].value = 'Loop_Deviation'
1492
                        ws['X1'].font = Font(bold=True, underline='single', size=11)
1493

    
1494
                        loops = app_doc_data.activeDrawing.loops
1495
                        strmsg = None
1496
                        row_no = 2
1497
                        for loop in loops:
1498
                            deviation = loop.deviation
1499
                            if round(deviation, 9) == 0:
1500
                                continue
1501

    
1502
                            for i in range(1, len(loop.items) - 1, 3):
1503
                                if i == 1:
1504
                                    loop_no = loop.name.replace('Loop', '')
1505
                                    if len(loop_no) > 1:
1506
                                        strmsg = '({}) {}'.format(loop_no, loop.items[i].stream_no)
1507
                                    else:
1508
                                        strmsg = '(0{}) {}'.format(loop_no, loop.items[i].stream_no)
1509
                                else:
1510
                                    if len(str(loop.items[i - 1])) > 13 and str(loop.items[i - 1])[
1511
                                                                            :13] == 'Line_Splitter':
1512
                                        pre_stream_num = loop.items[i - 3].stream_no
1513
                                        cur_stream_num = loop.items[i].stream_no
1514

    
1515
                                        if strmsg.__contains__('-'):
1516
                                            last_stream_num = str(strmsg).rsplit('-', 1)[1]
1517
                                        else:
1518
                                            last_stream_num = str(strmsg).rsplit(' ', 1)[1]
1519

    
1520
                                        if pre_stream_num != int(last_stream_num):
1521
                                            strmsg = '{}-{}-{}'.format(strmsg, pre_stream_num, cur_stream_num)
1522

    
1523
                            last_stream_num = loop.items[len(loop.items) - 2].stream_no
1524
                            strmsg = '{}-{}'.format(strmsg, last_stream_num)
1525

    
1526
                            value = '{} = {} {}'.format(strmsg, round(deviation, 3), units['Pressure'])
1527
                            if round(deviation, 3) < 0:
1528
                                ws.cell(row_no, 24, value).font = Font(underline='single',
1529
                                                                       size=10,
1530
                                                                       color='FF0000')
1531

    
1532
                            elif round(deviation, 3) > 0:
1533
                                ws.cell(row_no, 24, value).font = Font(size=10)
1534
                            else:
1535
                                ws.cell(row_no, 24, value).font = Font(underline='single',
1536
                                                                       size=10)
1537
                            row_no += 1
1538

    
1539
                        start_row_no = row_no + 1
1540
                        start_col_no = 24
1541
                        # Pump
1542
                        pumps = []
1543
                        row_no = start_row_no
1544
                        col_no = start_col_no
1545
                        for loop in loops:
1546
                            for item in loop.items:
1547
                                parent = item.parent
1548
                                if parent:
1549
                                    name = str(item).replace(
1550
                                        '_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
1551
                                    if name in pumps:
1552
                                        continue
1553
                                    if name[:3] == 'R_P' or name[:3] == 'L_P' or name[:3] == 'V_P':
1554
                                        pumps.append(name)
1555

    
1556
                                        attr = item.parent.attribute
1557
                                        ws.cell(row_no, col_no, attr['Tag_No']).font = Font(bold=True,
1558
                                                                                            underline='single',
1559
                                                                                            size=11)
1560

    
1561
                                        row_no += 1
1562
                                        ws.cell(row_no, col_no, 'Suct.P :')
1563
                                        col_no += 2
1564
                                        ws.cell(row_no, col_no, round(attr['Suct.P'], 3))
1565
                                        col_no += 1
1566
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1567

    
1568
                                        row_no += 1
1569
                                        col_no = 24 if col_no == 27 else 28
1570
                                        ws.cell(row_no, col_no, 'Disc.P :')
1571
                                        col_no += 2
1572
                                        ws.cell(row_no, col_no, round(attr['Disc.P'], 3))
1573
                                        col_no += 1
1574
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1575

    
1576
                                        row_no += 1
1577
                                        col_no = 24 if col_no == 27 else 28
1578
                                        ws.cell(row_no, col_no, 'Diff.P :')
1579
                                        col_no += 2
1580
                                        ws.cell(row_no, col_no, round(attr['Diff.P'], 3))
1581
                                        col_no += 1
1582
                                        ws.cell(row_no, col_no, units['Pressure'])
1583

    
1584
                                        row_no += 1
1585
                                        col_no = 24 if col_no == 27 else 28
1586
                                        ws.cell(row_no, col_no, 'Head :')
1587
                                        col_no += 2
1588
                                        ws.cell(row_no, col_no, round(attr['Head'], 3))
1589
                                        col_no += 1
1590
                                        ws.cell(row_no, col_no, units['Length'])
1591

    
1592
                                        row_no += 1
1593
                                        col_no = 24 if col_no == 27 else 28
1594
                                        ws.cell(row_no, col_no, 'NPSHa :')
1595
                                        col_no += 2
1596
                                        ws.cell(row_no, col_no, round(attr['NPSHa'], 3))
1597
                                        col_no += 1
1598
                                        ws.cell(row_no, col_no, units['Length'])
1599

    
1600
                                        row_no += 1
1601
                                        col_no = 24 if col_no == 27 else 28
1602
                                        ws.cell(row_no, col_no, 'Vap. P :')
1603
                                        col_no += 2
1604
                                        ws.cell(row_no, col_no, attr['Vap. P'])
1605
                                        col_no += 1
1606
                                        ws.cell(row_no, col_no, '{}.a'.format(units['Pressure']))
1607

    
1608
                                        row_no += 1
1609
                                        col_no = 24 if col_no == 27 else 28
1610
                                        ws.cell(row_no, col_no, 'HHP :')
1611
                                        col_no += 2
1612
                                        ws.cell(row_no, col_no, round(attr['HHP'], 3))
1613
                                        col_no += 1
1614
                                        ws.cell(row_no, col_no, units['Power'])
1615

    
1616
                                        col_no = 28 if col_no == 27 else 24
1617
                                        row_no = row_no - 7 if col_no == 28 else row_no + 2
1618

    
1619
                        start_row_no += math.ceil(len(pumps)) * 9
1620

    
1621
                        # Compressor
1622
                        compressors = []
1623
                        row_no = start_row_no
1624
                        col_no = start_col_no
1625
                        for loop in loops:
1626
                            for item in loop.items:
1627
                                parent = item.parent
1628
                                if parent:
1629
                                    name = str(item).replace(
1630
                                        '_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
1631
                                    if name in compressors:
1632
                                        continue
1633
                                    if name[:3] == 'R_K' or name[:3] == 'L_K':
1634
                                        compressors.append(name)
1635

    
1636
                                        attr = item.parent.attribute
1637
                                        ws.cell(row_no, col_no, attr['Tag_No']).font = Font(bold=True,
1638
                                                                                            underline='single',
1639
                                                                                            size=11)
1640

    
1641
                                        row_no += 1
1642
                                        ws.cell(row_no, col_no, 'Suct.P :')
1643
                                        col_no += 2
1644
                                        ws.cell(row_no, col_no, round(attr['Suct.P'], 3))
1645
                                        col_no += 1
1646
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1647

    
1648
                                        row_no += 1
1649
                                        col_no = 24 if col_no == 27 else 28
1650
                                        ws.cell(row_no, col_no, 'Disc.P :')
1651
                                        col_no += 2
1652
                                        ws.cell(row_no, col_no, round(attr['Disc.P'], 3))
1653
                                        col_no += 1
1654
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1655

    
1656
                                        row_no += 1
1657
                                        col_no = 24 if col_no == 27 else 28
1658
                                        ws.cell(row_no, col_no, 'Diff.P :')
1659
                                        col_no += 2
1660
                                        ws.cell(row_no, col_no, round(attr['Diff.P'], 3))
1661
                                        col_no += 1
1662
                                        ws.cell(row_no, col_no, units['Pressure'])
1663

    
1664
                                        row_no += 1
1665
                                        col_no = 24 if col_no == 27 else 28
1666
                                        ws.cell(row_no, col_no, 'HHP :')
1667
                                        col_no += 2
1668
                                        ws.cell(row_no, col_no, round(attr['HHP'], 3))
1669
                                        col_no += 1
1670
                                        ws.cell(row_no, col_no, units['Power'])
1671

    
1672
                                        col_no = 28 if col_no == 27 else 24
1673
                                        row_no = row_no - 4 if col_no == 28 else row_no + 2
1674

    
1675
                        start_row_no += math.ceil(len(compressors)) * 9
1676

    
1677
                        # Control Valve
1678
                        control_valves = []
1679
                        row_no = start_row_no
1680
                        col_no = start_col_no
1681
                        for loop in loops:
1682
                            for item in loop.items:
1683
                                parent = item.parent
1684
                                if parent:
1685
                                    name = str(item).replace(
1686
                                        '_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
1687
                                    if name in control_valves:
1688
                                        continue
1689
                                    if name[:3] == 'CV_':
1690
                                        control_valves.append(name)
1691

    
1692
                                        attr = item.parent.attribute
1693
                                        if 'Tag_No' in attr:
1694
                                            ws.cell(row_no, col_no, attr['Tag_No']).font = Font(bold=True,
1695
                                                                                                underline='single',
1696
                                                                                                size=11)
1697

    
1698
                                        row_no += 1
1699
                                        ws.cell(row_no, col_no, 'Inlet P :')
1700
                                        col_no += 2
1701
                                        if 'Suct.P' in attr:
1702
                                            ws.cell(row_no, col_no, round(attr['Suct.P'], 3))
1703
                                        col_no += 1
1704
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1705

    
1706
                                        row_no += 1
1707
                                        col_no = 24 if col_no == 27 else 28
1708
                                        ws.cell(row_no, col_no, 'Outlet P :')
1709
                                        col_no += 2
1710
                                        if 'Disc.P' in attr:
1711
                                            ws.cell(row_no, col_no, round(attr['Disc.P'], 3))
1712
                                        col_no += 1
1713
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1714

    
1715
                                        row_no += 1
1716
                                        col_no = 24 if col_no == 27 else 28
1717
                                        ws.cell(row_no, col_no, 'Diff.P :')
1718
                                        col_no += 2
1719
                                        if 'Diff.P' in attr:
1720
                                            ws.cell(row_no, col_no, round(attr['Diff.P'], 3))
1721
                                        col_no += 1
1722
                                        ws.cell(row_no, col_no, units['Pressure'])
1723

    
1724
                                        row_no += 1
1725
                                        col_no = 24 if col_no == 27 else 28
1726
                                        ws.cell(row_no, col_no, 'dP Ratio :')
1727
                                        col_no += 2
1728
                                        if 'dP Ratio' in attr:
1729
                                            ws.cell(row_no, col_no, round(attr['dP Ratio'] * 100, 2))
1730
                                        col_no += 1
1731
                                        ws.cell(row_no, col_no, '%')
1732

    
1733
                                        col_no = 28 if col_no == 27 else 24
1734
                                        row_no = row_no - 4 if col_no == 28 else row_no + 2
1735

    
1736
                        # write hmb unit
1737
                        ws['B55'].value = '-'
1738
                        ws['B56'].value = units['Pipe_Diameter']
1739
                        ws['B57'].value = units['Flowrate_Mass']
1740
                        ws['B58'].value = units['Flowrate_Volume']
1741
                        ws['B59'].value = units['Density']
1742
                        ws['B60'].value = units['Viscosity']
1743
                        ws['B61'].value = units['Temperature']  # Hidden
1744
                        ws['B62'].value = ''  # Hidden
1745
                        ws['B63'].value = ''  # Hidden
1746
                        ws['B64'].value = ''  # Hidden
1747
                        ws['B65'].value = units['Pipe_Diameter']  # Hidden
1748
                        ws['B66'].value = units['Pipe_Diameter']  # Hidden
1749
                        ws['B67'].value = ''  # Hidden
1750
                        ws['B68'].value = units['Length']
1751
                        ws['B69'].value = units['Length']
1752
                        ws['B70'].value = units['Roughness']
1753
                        ws['B71'].value = units['Velocity']  # Hidden
1754
                        ws['B72'].value = '{}/100{})'.format(units['Pressure'], units['Length'])  # Hidden
1755
                        ws['B74'].value = units['Velocity']
1756
                        ws['B75'].value = ''  # Hidden
1757
                        ws['B76'].value = ''  # Hidden
1758
                        ws['B77'].value = '{}/100{}'.format(units['Pressure'], units['Length'])
1759
                        ws['B78'].value = units['Pressure']
1760
                        ws['B79'].value = units['Pressure']
1761
                        ws['B80'].value = '{}(g)'.format(units['Pressure'])
1762

    
1763
                        page_no += 1
1764

    
1765
                    current_ws = wb.get_sheet_by_name('Page (1)')
1766
                    # write hmb data
1767
                    drawing = app_doc_data.activeDrawing
1768
                    hmbs = drawing.hmbTable._hmbs
1769
                    if hmbs is not None:
1770
                        stream_count = 1
1771
                        col_no = 3
1772
                        _hmbs = sorted(hmbs, key=lambda hmb: hmb.stream_no)
1773
                        for hmb in _hmbs:
1774
                            if hmb.isDeleted == True:
1775
                                continue
1776

    
1777
                            ws = self.get_work_sheet(wb, stream_count)
1778
                            if ws != current_ws:
1779
                                current_ws = ws
1780
                                col_no = 3
1781

    
1782
                            row_no = 54
1783
                            ws.cell(row_no, col_no, hmb.stream_no)
1784
                            row_no += 1
1785
                            ws.cell(row_no, col_no, hmb.phase_type)
1786
                            row_no += 1
1787
                            ws.cell(row_no, col_no, hmb.inside_pipe_size)
1788
                            row_no += 1
1789
                            ws.cell(row_no, col_no, hmb.flowrate_mass)
1790
                            row_no += 1
1791
                            ws.cell(row_no, col_no, hmb.flowrate_volume)
1792
                            row_no += 1
1793
                            ws.cell(row_no, col_no, hmb.density)
1794
                            row_no += 1
1795
                            ws.cell(row_no, col_no, hmb.viscosity)
1796
                            row_no += 1
1797
                            ws.cell(row_no, col_no, hmb.temperature)
1798
                            row_no += 1
1799
                            ws.cell(row_no, col_no, hmb.molecular_weight)
1800
                            row_no += 1
1801
                            ws.cell(row_no, col_no, hmb.specific_heat_ratio)
1802
                            row_no += 1
1803
                            ws.cell(row_no, col_no, hmb.compress_factor)
1804
                            row_no += 1
1805
                            ws.cell(row_no, col_no, hmb.nominal_pipe_size)
1806
                            row_no += 1
1807
                            ws.cell(row_no, col_no, hmb.inside_pipe_size)
1808
                            row_no += 1
1809
                            ws.cell(row_no, col_no, hmb.schedule_no)
1810
                            row_no += 1
1811
                            ws.cell(row_no, col_no, hmb.straight_length)
1812
                            row_no += 1
1813
                            ws.cell(row_no, col_no, hmb.equivalent_length)
1814
                            row_no += 1
1815
                            ws.cell(row_no, col_no, hmb.roughness)
1816
                            row_no += 1
1817
                            ws.cell(row_no, col_no, hmb.limitation_velocity)
1818
                            row_no += 1
1819
                            ws.cell(row_no, col_no, hmb.limitation_pressure_drop)
1820
                            row_no += 2
1821
                            ws.cell(row_no, col_no, hmb.velocity)
1822
                            row_no += 1
1823
                            ws.cell(row_no, col_no, hmb.reynolds)
1824
                            row_no += 1
1825
                            ws.cell(row_no, col_no, hmb.friction_factor)
1826
                            row_no += 1
1827
                            ws.cell(row_no, col_no, hmb.pressure_drop)
1828
                            row_no += 1
1829
                            ws.cell(row_no, col_no, hmb.pressure_drop_friction)
1830
                            row_no += 1
1831
                            ws.cell(row_no, col_no, hmb.pressure_drop_static)
1832
                            row_no += 1
1833
                            ws.cell(row_no, col_no, hmb.pressure_pipe_end_point)
1834

    
1835
                            col_no += 1
1836
                            stream_count += 1
1837

    
1838
                    # two_phase
1839
                    if hmbs is not None:
1840
                        for hmb in hmbs:
1841
                            if hmb.phase_type == 'Mixed':
1842
                                lines = [item for item in self.graphicsView.scene.items() if type(
1843
                                    item) is QEngineeringStreamlineItem and str(item.uid) == str(hmb.components_uid)]
1844
                                if lines:
1845
                                    ws = wb.copy_worksheet(wb.get_sheet_by_name('Two_phase'))
1846
                                    ws.title = 'Two Phase {}'.format(str(lines[0]))
1847

    
1848
                                    # Information
1849
                                    ws['B5'].value = 'File : Two_Phase'
1850
                                    ws['B6'].value = 'Line : {}'.format(lines[0].stream_no)
1851
                                    ws['G6'].value = 'Made by : {}'.format(configs[0].value)
1852

    
1853
                                    # Process Data
1854
                                    ws['C10'].value = units['Flowrate_Mass']
1855
                                    ws['D10'].value = units['Density']
1856
                                    ws['E10'].value = units['Viscosity']
1857
                                    ws['F10'].value = '{}(g)'.format(units['Pressure'])
1858
                                    ws['G10'].value = units['Temperature']
1859

    
1860
                                    ws['C11'].value = lines[0].data.liquid_flowrate_mass
1861
                                    ws['D11'].value = lines[0].data.liquid_density
1862
                                    ws['E11'].value = lines[0].data.liquid_viscosity
1863

    
1864
                                    ws['C12'].value = lines[0].data.vapor_flowrate_mass
1865
                                    ws['D12'].value = lines[0].data.vapor_density
1866
                                    ws['E12'].value = lines[0].data.vapor_viscosity
1867
                                    ws['F12'].value = lines[0].data.vapor_pressure
1868
                                    ws['G12'].value = lines[0].data.vapor_temperature
1869
                                    ws['H12'].value = lines[0].data.vapor_molecular_weight
1870
                                    ws['I12'].value = lines[0].data.vapor_compress_factor
1871

    
1872
                                    # Geometry Data
1873
                                    ws['D16'].value = units['Pipe_Diameter']
1874
                                    ws['E16'].value = units['Roughness']
1875
                                    ws['F16'].value = units['Length']
1876

    
1877
                                    row_no = 17
1878
                                    for geometry in lines[0].mixed_geometry:
1879
                                        col_no = 3
1880
                                        ws.cell(row_no, col_no, str(geometry[0]))  # Element
1881
                                        col_no += 1
1882
                                        ws.cell(row_no, col_no,
1883
                                                float(geometry[3]) if is_float(geometry[3]) else None)  # ID
1884
                                        col_no += 1
1885
                                        ws.cell(row_no, col_no,
1886
                                                float(geometry[4]) if is_float(geometry[4]) else None)  # Roughness
1887
                                        col_no += 1
1888
                                        ws.cell(row_no, col_no,
1889
                                                float(geometry[5]) if is_float(geometry[5]) else None)  # Length
1890
                                        col_no += 1
1891
                                        ws.cell(row_no, col_no,
1892
                                                float(geometry[6]) if is_float(geometry[6]) else None)  # Angle
1893
                                        col_no += 1
1894
                                        ws.cell(row_no, col_no,
1895
                                                float(geometry[7]) if is_float(geometry[7]) else None)  # R/D
1896
                                        col_no += 1
1897
                                        ws.cell(row_no, col_no,
1898
                                                float(geometry[9]) if is_float(geometry[9]) else None)  # K
1899

    
1900
                                        row_no += 1
1901

    
1902

    
1903
                                    # horizontal_flow_image = openpyxl.drawing.image.Image(horizontal_flow_image_path)
1904
                                    # horizontal_flow_image.width *= 0.6
1905
                                    # horizontal_flow_image.height *= 0.6
1906
                                    # ws.add_image(horizontal_flow_image, 'L3')
1907

    
1908
                                    # vertical_upward_flow_image = openpyxl.drawing.image.Image(
1909
                                    #     vertical_upward_flow_image_path)
1910
                                    # vertical_upward_flow_image.width *= 0.6
1911
                                    # vertical_upward_flow_image.height *= 0.6
1912
                                    # ws.add_image(vertical_upward_flow_image, 'R3')
1913

    
1914
                                    # vertical_downward_flow_image = openpyxl.drawing.image.Image(
1915
                                    #     vertical_downward_flow_image_path)
1916
                                    # vertical_downward_flow_image.width *= 0.6
1917
                                    # vertical_downward_flow_image.height *= 0.6
1918
                                    # ws.add_image(vertical_downward_flow_image, 'Z3')
1919

    
1920
                                    horizontal = DrawImage(horizontal_flow_image_path)
1921
                                    vertical_upward = DrawImage(vertical_upward_flow_image_path)
1922
                                    vertical_downward = DrawImage(vertical_downward_flow_image_path)
1923

    
1924
                                    # Calculation Result
1925
                                    MainWindow.write_calculation_result_units(ws, units)
1926

    
1927
                                    row_no = 37
1928
                                    for row in lines[0].mixed_pressure_variation:
1929
                                        col_no = 12
1930
                                        ws.cell(row_no, col_no, str(row[0]))    # Element
1931
                                        col_no += 1
1932
                                        ws.cell(row_no, col_no, row[1])         # ID
1933
                                        col_no += 1
1934
                                        ws.cell(row_no, col_no, row[2])         # Length
1935
                                        col_no += 1
1936
                                        ws.cell(row_no, col_no, row[3])         # Angle
1937
                                        col_no += 1
1938
                                        ws.cell(row_no, col_no, row[4])         # K
1939
                                        col_no += 1
1940
                                        ws.cell(row_no, col_no, row[5])         # Pressure
1941
                                        col_no += 1
1942
                                        ws.cell(row_no, col_no, row[6])         # Void
1943
                                        col_no += 1
1944
                                        ws.cell(row_no, col_no, row[7])         # Quality
1945
                                        col_no += 1
1946
                                        ws.cell(row_no, col_no, row[8])         # Mean. Density
1947
                                        col_no += 1
1948
                                        ws.cell(row_no, col_no, row[9])         # Homo. Density
1949
                                        col_no += 1
1950
                                        ws.cell(row_no, col_no, row[10])        # V.Density
1951
                                        col_no += 1
1952
                                        ws.cell(row_no, col_no, row[11])        # Mean.Vel
1953
                                        col_no += 1
1954
                                        ws.cell(row_no, col_no, row[12])        # Homo.Vel
1955
                                        col_no += 1
1956
                                        ws.cell(row_no, col_no, row[13])        # Max.Vel
1957
                                        col_no += 1
1958
                                        ws.cell(row_no, col_no, row[14])        # Ero.Vel
1959
                                        col_no += 1
1960
                                        ws.cell(row_no, col_no, row[15])        # Pattern X
1961
                                        col_no += 1
1962
                                        ws.cell(row_no, col_no, row[16])        # Pattern Y
1963
                                        col_no += 1
1964
                                        ws.cell(row_no, col_no, str(row[17]) if row[17] else '')    # Regime
1965
                                        col_no += 1
1966
                                        ws.cell(row_no, col_no, row[18])        # Friction
1967
                                        col_no += 1
1968
                                        ws.cell(row_no, col_no, row[19])        # Gravity
1969
                                        col_no += 1
1970
                                        ws.cell(row_no, col_no, row[20])        # Moment
1971
                                        col_no += 1
1972
                                        ws.cell(row_no, col_no, row[21])        # Total
1973

    
1974
                                        # load_regime
1975
                                        length = row[2]
1976
                                        if length is not None:
1977
                                            x = float(row[15])
1978
                                            y = float(row[16])
1979

    
1980
                                            angle = float(row[3])
1981
                                            if angle == 0:
1982
                                                # Horizontal
1983
                                                origin_x = 57
1984
                                                origin_y = 652
1985

    
1986
                                                image_min_x = 57
1987
                                                image_min_y = 2
1988
                                                image_max_x = 603
1989
                                                image_max_y = 651
1990
                                                left = origin_x + ((image_max_x - image_min_x) / (7 - (-5))) * (
1991
                                                            math.log10(x) - (-5))
1992
                                                top = origin_y - ((image_max_y - image_min_y) / (5 - (-9))) * (
1993
                                                            math.log10(y) - (-9))
1994

    
1995
                                                horizontal.draw_regime(left, top, 8)
1996
                                                # horizontal.draw_regime(origin_x, origin_y, 8) # 0, 0 위치 확인 방법
1997
                                            elif angle > 0:
1998
                                                # Vertical Upward
1999
                                                origin_x = 59
2000
                                                origin_y = 681
2001

    
2002
                                                image_min_x = 60
2003
                                                image_min_y = 29
2004
                                                image_max_x = 596
2005
                                                image_max_y = 681
2006
                                                left = origin_x + ((image_max_x - image_min_x) / (7 - (-2))) * (
2007
                                                            math.log10(x) - (-2))
2008
                                                top = origin_y - ((image_max_y - image_min_y) / (8 - (-5))) * (
2009
                                                            math.log10(y) - (-5))
2010

    
2011
                                                vertical_upward.draw_regime(left, top, 8)
2012
                                                # vertical_upward.draw_regime(origin_x, origin_y, 8) # 0, 0 위치 확인 방법
2013
                                            elif angle < 0:
2014
                                                # Vertical Downward
2015
                                                origin_x = 55
2016
                                                origin_y = 656
2017

    
2018
                                                image_min_x = 55
2019
                                                image_min_y = 21
2020
                                                image_max_x = 572
2021
                                                image_max_y = 656
2022
                                                left = origin_x + ((image_max_x - image_min_x) / (20 - 0)) * (x - 0)
2023
                                                top = origin_y - ((image_max_y - image_min_y) / (24 - 0)) * (y - 0)
2024

    
2025
                                                vertical_downward.draw_regime(left, top, 8)
2026
                                                # vertical_downward.draw_regime(origin_x, origin_y, 8) # 0, 0 위치 확인 방법
2027

    
2028
                                        row_no += 1
2029

    
2030
                                    with NamedTemporaryFile() as f:
2031
                                        horizontal_image_path = f.name + '.png'
2032
                                    horizontal.save_as(horizontal_image_path)
2033

    
2034
                                    horizontal_flow_image = openpyxl.drawing.image.Image(horizontal_image_path)
2035
                                    horizontal_flow_image.width *= 0.6
2036
                                    horizontal_flow_image.height *= 0.6
2037
                                    ws.add_image(horizontal_flow_image, 'L4')
2038

    
2039
                                    with NamedTemporaryFile() as f:
2040
                                        vertical_upward_image_path = f.name + '.png'
2041
                                    vertical_upward.save_as(vertical_upward_image_path)
2042

    
2043
                                    vertical_upward_flow_image = openpyxl.drawing.image.Image(
2044
                                        vertical_upward_image_path)
2045
                                    vertical_upward_flow_image.width *= 0.6
2046
                                    vertical_upward_flow_image.height *= 0.6
2047
                                    ws.add_image(vertical_upward_flow_image, 'S3')
2048

    
2049
                                    with NamedTemporaryFile() as f:
2050
                                        vertical_downward_image_path = f.name + '.png'
2051
                                    vertical_downward.save_as(vertical_downward_image_path)
2052

    
2053
                                    vertical_downward_flow_image = openpyxl.drawing.image.Image(
2054
                                        vertical_downward_image_path)
2055
                                    vertical_downward_flow_image.width *= 0.6
2056
                                    vertical_downward_flow_image.height *= 0.6
2057
                                    ws.add_image(vertical_downward_flow_image, 'AA3')
2058

    
2059
                    active_sheet = wb.get_sheet_by_name('Page (1)')
2060
                    if active_sheet:
2061
                        wb.active = active_sheet
2062

    
2063
                    wb.get_sheet_by_name('Page').sheet_state = 'hidden'
2064
                    wb.get_sheet_by_name('Two_phase').sheet_state = 'hidden'
2065

    
2066
                    name_without_ext, ext = os.path.splitext(file_name)
2067
                    save_file_name = file_name if ext.upper() == '.XLSX' else name_without_ext + '.xlsx'
2068
                    wb.save(filename=save_file_name)
2069

    
2070
                    os.startfile(save_file_name)
2071

    
2072
                    QMessageBox.information(self, self.tr('Information'), self.tr('Report is done'))
2073
        except Exception as ex:
2074
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2075
                      f"{sys.exc_info()[-1].tb_lineno}"
2076
            self.addMessage.emit(MessageType.Error, message)
2077

    
2078
    @staticmethod
2079
    def write_calculation_result_units(ws, units) -> None:
2080
        """write calculation result unit for 2-phase"""
2081

    
2082
        row_no = 36
2083
        ws.cell(row_no, 13, units['Pipe_Diameter'])  # ID
2084
        ws.cell(row_no, 14, units['Length'])  # Length
2085
        ws.cell(row_no, 17, units['Pressure'])  # Pressure
2086
        ws.cell(row_no, 20, units['Density'])  # Mean. Density
2087
        ws.cell(row_no, 21, units['Density'])  # Homo. Density
2088
        ws.cell(row_no, 22, units['Density'])  # V.Density
2089
        ws.cell(row_no, 23, units['Velocity'])  # Mean.Vel
2090
        ws.cell(row_no, 24, units['Velocity'])  # Homo.Vel
2091
        ws.cell(row_no, 25, units['Velocity'])  # Max.Vel
2092
        ws.cell(row_no, 26, units['Velocity'])  # Ero.Vel
2093

    
2094
        ws.cell(35, 30, f"Pressure Drop ({units['Pressure']}/{units['Length']})")  # Pressure Drop
2095
        ws.cell(row_no, 33, units['Pressure'])  # Total
2096

    
2097
    def get_work_sheet(self, work_book, count):
2098
        page_no = math.ceil(count / 20)
2099
        return work_book.get_sheet_by_name('Page ({})'.format(page_no))
2100

    
2101
    def display_output(self):
2102
        from HydroCalculationCommand import HydroCalculationCommand
2103
        from Outputs import Output
2104

    
2105
        try:
2106
            """ display output """
2107
            app_doc_data = AppDocData.instance()
2108
            drawing = app_doc_data.activeDrawing
2109
            if drawing is None:
2110
                return
2111

    
2112
            self.clear_output()
2113
            if not HydroCalculationCommand.ERRORS:
2114
                self.tableWidgetOutput.setColumnCount(3)
2115
                self.tableWidgetOutput.horizontalHeader().setVisible(False)
2116
                self.tableWidgetOutput.verticalHeader().setVisible(False)
2117
                self.tableWidgetOutput.setSelectionMode(QAbstractItemView.SingleSelection)
2118
                self.tableWidgetOutput.setSelectionBehavior(QAbstractItemView.SelectRows)
2119
                self.tableWidgetOutput.setEditTriggers(QAbstractItemView.NoEditTriggers)
2120
                self.tableWidgetOutput.horizontalHeader().setStretchLastSection(True)
2121

    
2122
                self.tableWidgetDeviation.setColumnCount(1)
2123
                self.tableWidgetDeviation.horizontalHeader().setVisible(False)
2124
                self.tableWidgetDeviation.verticalHeader().setVisible(False)
2125
                self.tableWidgetDeviation.setSelectionMode(QAbstractItemView.SingleSelection)
2126
                self.tableWidgetDeviation.setSelectionBehavior(QAbstractItemView.SelectRows)
2127
                self.tableWidgetDeviation.setEditTriggers(QAbstractItemView.NoEditTriggers)
2128
                self.tableWidgetDeviation.horizontalHeader().setStretchLastSection(True)
2129
                self.tableWidgetDeviation.resizeRowsToContents()
2130
                self.tableWidgetDeviation.resizeColumnsToContents()
2131

    
2132
                units = [attr[1] for attr in drawing.attrs if attr[0] == 'Units'][0]
2133

    
2134
                loops = drawing.loops
2135

    
2136
                # Deviation
2137
                self.add_data(self.tableWidgetDeviation, 'Loop_Deviation', None, None, True, None, None)
2138
                strmsg = None
2139
                for loop in loops:
2140
                    deviation = loop.deviation
2141
                    if round(deviation, 9) == 0:
2142
                        continue
2143

    
2144
                    for i in range(1, len(loop.items) - 1, 3):
2145
                        if i == 1:
2146
                            loop_no = loop.name.replace('Loop', '')
2147
                            if len(loop_no) > 1:
2148
                                strmsg = '({}) {}'.format(loop_no, loop.items[i].stream_no)
2149
                            else:
2150
                                strmsg = '(0{}) {}'.format(loop_no, loop.items[i].stream_no)
2151
                        else:
2152
                            if len(str(loop.items[i - 1])) > 13 and str(loop.items[i - 1])[:13] == 'Line_Splitter':
2153
                                pre_stream_num = loop.items[i - 3].stream_no
2154
                                cur_stream_num = loop.items[i].stream_no
2155

    
2156
                                if strmsg.__contains__('-'):
2157
                                    last_stream_num = str(strmsg).rsplit('-', 1)[1]
2158
                                else:
2159
                                    last_stream_num = str(strmsg).rsplit(' ', 1)[1]
2160

    
2161
                                if pre_stream_num != int(last_stream_num):
2162
                                    strmsg = '{}-{}-{}'.format(strmsg, pre_stream_num, cur_stream_num)
2163

    
2164
                    last_stream_num = loop.items[len(loop.items) - 2].stream_no
2165
                    strmsg = '{}-{}'.format(strmsg, last_stream_num)
2166

    
2167
                    if round(deviation, 3) < 0:
2168
                        self.add_data(self.tableWidgetDeviation,
2169
                                      '{} = {} {}'.format(strmsg, round(deviation, 3), units['Pressure']), None, None, None,
2170
                                      None, QBrush(QColor(255, 0, 0)))
2171
                    else:
2172
                        self.add_data(self.tableWidgetDeviation,
2173
                                      '{} = {} {}'.format(strmsg, round(deviation, 3), units['Pressure']), None, None, None,
2174
                                      None)
2175

    
2176
                names = []
2177
                # Pump
2178
                for loop in loops:
2179
                    for item in loop.items:
2180
                        parent = item.parent
2181
                        if parent:
2182
                            name = str(item).replace('_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
2183
                            if name in names:
2184
                                continue
2185
                            if name[:3] == 'R_P' or name[:3] == 'L_P' or name[:3] == 'V_P':
2186
                                names.append(name)
2187
                                attr = item.parent.attribute
2188
                                if len(attr) > 0:
2189
                                    """store calculation output for pump"""
2190
                                    output = Output()
2191
                                    output.components_uid = str(item.parent.uid)
2192
                                    output.suctp = round(attr['Suct.P'], 3)
2193
                                    output.discp = round(attr['Disc.P'], 3)
2194
                                    output.diffp = round(attr['Diff.P'], 3)
2195
                                    output.head = round(attr['Head'], 3)
2196
                                    output.npsha = round(attr['NPSHa'], 3)
2197
                                    output.vapp = attr['Vap. P']
2198
                                    output.hhp = round(attr['HHP'], 3)
2199
                                    app_doc_data.outputs.append(output)
2200
                                    """up to here"""
2201

    
2202
                                    self.add_data(self.tableWidgetOutput, attr['Tag_No'], None, None, True, True)
2203
                                    self.add_data(self.tableWidgetOutput, 'Suct.P :', output.suctp,
2204
                                                  '{}.g'.format(units['Pressure']))
2205
                                    self.add_data(self.tableWidgetOutput, 'Disc.P :', output.discp,
2206
                                                  '{}.g'.format(units['Pressure']))
2207
                                    self.add_data(self.tableWidgetOutput, 'Diff.P :', output.diffp,
2208
                                                  units['Pressure'])
2209
                                    self.add_data(self.tableWidgetOutput, 'Head :', output.head, units['Length'])
2210
                                    self.add_data(self.tableWidgetOutput, 'NPSHa :', output.npsha, units['Length'])
2211
                                    self.add_data(self.tableWidgetOutput, 'Vap. P :', output.vapp,
2212
                                                  '{}.a'.format(units['Pressure']))
2213
                                    self.add_data(self.tableWidgetOutput, 'HHP :', output.hhp, units['Power'])
2214

    
2215

    
2216
                # Compressor
2217
                for loop in loops:
2218
                    for item in loop.items:
2219
                        parent = item.parent
2220
                        if parent:
2221
                            name = str(item).replace('_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
2222
                            if name in names:
2223
                                continue
2224
                            if name[:3] == 'R_K' or name[:3] == 'L_K':
2225
                                names.append(name)
2226

    
2227
                                attr = item.parent.attribute
2228
                                if len(attr) > 0:
2229
                                    """store calculation output for compressor"""
2230
                                    output = Output()
2231
                                    output.components_uid = str(item.parent.uid)
2232
                                    output.suctp = round(attr['Suct.P'], 3)
2233
                                    output.discp = round(attr['Disc.P'], 3)
2234
                                    output.diffp = round(attr['Diff.P'], 3)
2235
                                    output.hhp = round(attr['HHP'], 3)
2236
                                    app_doc_data.outputs.append(output)
2237
                                    """up to here"""
2238

    
2239
                                    self.add_data(self.tableWidgetOutput, attr['Tag_No'], None, None, True, True)
2240
                                    self.add_data(self.tableWidgetOutput, 'Suct.P :', output.suctp,
2241
                                                  '{}.g'.format(units['Pressure']))
2242
                                    self.add_data(self.tableWidgetOutput, 'Disc.P :', output.discp,
2243
                                                  '{}.g'.format(units['Pressure']))
2244
                                    self.add_data(self.tableWidgetOutput, 'Diff.P :', output.diffp,
2245
                                                  units['Pressure'])
2246
                                    self.add_data(self.tableWidgetOutput, 'HHP :', output.hhp, units['Power'])
2247

    
2248
                # Control Valve
2249
                for loop in loops:
2250
                    for item in loop.items:
2251
                        parent = item.parent
2252
                        if parent:
2253
                            name = str(item).replace('_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
2254
                            if name in names:
2255
                                continue
2256
                            if name[:3] == 'CV_':
2257
                                names.append(name)
2258

    
2259
                                attr = item.parent.attribute
2260
                                if len(attr) > 0:
2261
                                    """store calculation output for control valve"""
2262
                                    output = Output()
2263
                                    output.components_uid = str(item.parent.uid)
2264
                                    output.suctp = round(attr['Suct.P'], 3)
2265
                                    output.discp = round(attr['Disc.P'], 3)
2266
                                    output.diffp = round(attr['Diff.P'], 3)
2267
                                    output.dpratio = round(attr['dP Ratio'] * 100, 2)
2268
                                    app_doc_data.outputs.append(output)
2269
                                    """up to here"""
2270

    
2271
                                    self.add_data(self.tableWidgetOutput, attr['Tag_No'], None, None, True, True)
2272
                                    self.add_data(self.tableWidgetOutput, 'Inlet P :', output.suctp,
2273
                                                  '{}.g'.format(units['Pressure']))
2274
                                    self.add_data(self.tableWidgetOutput, 'Outlet P :', output.discp,
2275
                                                  '{}.g'.format(units['Pressure']))
2276
                                    self.add_data(self.tableWidgetOutput, 'Diff.P :', output.diffp,
2277
                                                  units['Pressure'])
2278
                                    self.add_data(self.tableWidgetOutput, 'dP Ratio :', output.dpratio, '%')
2279
        except Exception as ex:
2280
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2281
                      f"{sys.exc_info()[-1].tb_lineno}"
2282
            self.addMessage.emit(MessageType.Error, message)
2283

    
2284
    def display_loops(self):
2285
        """ display loops """
2286
        from HydroCalculationCommand import HydroCalculationCommand
2287

    
2288
        try:
2289
            drawing = AppDocData.instance().activeDrawing
2290
            if drawing is None: return
2291

    
2292
            self.tableWidgetLoop.clear()
2293

    
2294
            loops = drawing.loops
2295
            if loops and not HydroCalculationCommand.ERRORS:
2296
                self.tableWidgetLoop.setColumnCount(len(loops) * 5)
2297

    
2298
                _col_names = [[loop.name, 'pressure', 'Static\nLine\ndP_Eq', 'El.\nDensity\nEl.', 'Extra'] for loop in
2299
                              loops]
2300
                col_names = []
2301
                for col_name in _col_names: col_names.extend(col_name)
2302
                self.tableWidgetLoop.setHorizontalHeaderLabels(col_names)
2303
                self.tableWidgetLoop.horizontalHeader().setVisible(True)
2304

    
2305
                max_rows = 0
2306
                for col in range(len(loops)):
2307
                    rows = len(loops[col].items)
2308
                    max_rows = max(max_rows, rows)
2309
                    self.tableWidgetLoop.setRowCount(max_rows)
2310

    
2311
                    for row in range(len(loops[col].items)):
2312
                        item = QTableWidgetItem(str(loops[col].items[row]))
2313
                        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
2314
                        self.tableWidgetLoop.setItem(row, col * 5, item)
2315

    
2316
                    # display calculation values
2317
                    for row in range(len(loops[col].items)):
2318
                        if loops[col].items[row] in loops[col].pressures:
2319
                            pressure = loops[col].pressures[loops[col].items[row]] \
2320
                                if loops[col].pressures[loops[col].items[row]] else 0
2321
                            item = QTableWidgetItem(str(round(pressure, 8)))
2322
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2323
                            # if not loops[col].pressures[loops[col].items[row]]: item.setBackground(Qt.red)
2324
                            self.tableWidgetLoop.setItem(row, col * 5 + 1, item)
2325

    
2326
                        if loops[col].items[row] in loops[col].pressure_drops:
2327
                            item = QTableWidgetItem(str(round(loops[col].pressure_drops[loops[col].items[row]], 9)))
2328
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2329
                            # if not loops[col].pressure_drops[loops[col].items[row]]: item.setBackground(Qt.red)
2330
                            self.tableWidgetLoop.setItem(row, col * 5 + 2, item)
2331

    
2332
                        if loops[col].items[row] in loops[col].density_elevations:
2333
                            item = QTableWidgetItem(str(round(loops[col].density_elevations[loops[col].items[row]], 8)))
2334
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2335
                            # if not loops[col].density_elevations[loops[col].items[row]]: item.setBackground(Qt.red)
2336
                            self.tableWidgetLoop.setItem(row, col * 5 + 3, item)
2337

    
2338
                        if loops[col].items[row] in loops[col].extras:
2339
                            item = QTableWidgetItem(str(loops[col].extras[loops[col].items[row]]))
2340
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2341
                            # if not loops[col].density_elevations[loops[col].items[row]]: item.setBackground(Qt.red)
2342
                            self.tableWidgetLoop.setItem(row, col * 5 + 4, item)
2343

    
2344
                self.tableWidgetLoop.resizeColumnsToContents()
2345
                self.tableWidgetLoop.resizeRowsToContents()
2346
        except Exception as ex:
2347
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2348
                      f"{sys.exc_info()[-1].tb_lineno}"
2349
            self.addMessage.emit(MessageType.Error, message)
2350

    
2351
    def configuration(self):
2352
        """configuration"""
2353
        from ConfigurationDialog import QConfigurationDialog
2354
        from Calculation import Conversion
2355

    
2356
        try:
2357
            appDocData = AppDocData.instance()
2358
            if appDocData.activeDrawing is None:
2359
                self.showImageSelectionMessageBox()
2360
                return
2361

    
2362
            dlg = QConfigurationDialog(self)
2363
            if QDialog.Accepted == dlg.show_dialog():
2364
                if dlg.need_to_convert:
2365
                    Conversion(dlg.decimal_point)
2366
                    self.actionSaveCliked(show_message=False)
2367
                    self.load_HMB()
2368

    
2369
                self.reload_units()
2370
                self.update_label_contents()
2371
                self.change_output_font_color()
2372
        except Exception as ex:
2373
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2374
                                                           sys.exc_info()[-1].tb_lineno)
2375
            self.addMessage.emit(MessageType.Error, message)
2376

    
2377
    def showImageSelectionMessageBox(self):
2378
        """Show Image Selection Guide MessageBox"""
2379
        QMessageBox.information(self, self.tr("Information"), self.tr("First of all, please open a drawing"))
2380

    
2381
    def display_colors(self, value):
2382
        """ display colors """
2383
        from DisplayColors import DisplayColors
2384
        from DisplayColors import DisplayOptions
2385

    
2386
        DisplayColors.instance().option = DisplayOptions.DisplayByLineNo if value == True else DisplayOptions.DisplayByLineType
2387
        if hasattr(self, 'graphicsView'):
2388
            self.graphicsView.scene.update(self.graphicsView.sceneRect())
2389
            DisplayColors.instance().save_data()
2390

    
2391
    '''
2392
        @brief      Open Border file 
2393
        @author     yeonjin
2394
        @date       2019.07.10
2395
    '''
2396

    
2397
    def open_border_file(self):
2398
        self.fitWindow(None)
2399

    
2400
    def patch_data(self):
2401
        '''apply path data'''
2402
        try:
2403
            app_doc_data = AppDocData.instance()
2404
            file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Scripts', 'patch.sql')
2405
            if os.path.isfile(file_path):
2406
                with open(file_path, 'rb') as file:
2407
                    scripts = file.read().decode('utf-8')
2408
                    app_doc_data.apply_path_data(scripts)
2409

    
2410
            find_column_names = ['Homo_Den', 'Homo_Vel']
2411
            column_names = app_doc_data.get_column_names('PressureVariation')
2412
            for column_name in find_column_names:
2413
                if column_name not in column_names:
2414
                    app_doc_data.add_column(column_name, 'REAL', 'PressureVariation')
2415

    
2416
            find_column_names = ['Input_Flowrate_Type']
2417
            column_names = app_doc_data.get_column_names('HMB')
2418
            for column_name in find_column_names:
2419
                if column_name not in column_names:
2420
                    app_doc_data.add_column(column_name, 'TEXT', 'HMB')
2421

    
2422
        except Exception as ex:
2423
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2424
                      f"{sys.exc_info()[-1].tb_lineno}"
2425
            self.addMessage.emit(MessageType.Error, message)
2426

    
2427
    def setAttributes(self, drawing):
2428
        drawing.setAttributes()
2429

    
2430
    '''
2431
        @brief      Reload HMB Units 
2432
        @author     yeonjin
2433
        @date       2019.07.10
2434
    '''
2435
    def reload_units(self):
2436
        from Drawing import Drawing
2437

    
2438
        try:
2439
            app_doc_data = AppDocData.instance()
2440
            drawing = app_doc_data.activeDrawing
2441

    
2442
            self.setAttributes(drawing)
2443

    
2444
            columnInfos = app_doc_data.getHMBDisplayNameAndUnitsExpression()
2445

    
2446
            rowIndex = 0
2447
            for row in range(columnInfos.rowCount()):
2448
                name = columnInfos.item(row, 0).text()
2449
                unit = self.convertToUnits(columnInfos.item(row, 1).text())
2450

    
2451
                self.tableWidgetHMB.setItem(rowIndex, 0,
2452
                                            set_item_properties(name, Qt.AlignLeft | Qt.AlignVCenter,
2453
                                                                QColor(51, 153, 102)))
2454
                # QColor(230, 230, 230)))
2455
                self.tableWidgetHMB.setItem(rowIndex, 1,
2456
                                            set_item_properties(unit, Qt.AlignHCenter | Qt.AlignVCenter,
2457
                                                                QColor(204, 255, 204)))
2458
                # QColor(230, 230, 230)))
2459

    
2460
                rowIndex += 1
2461

    
2462
            self.tableWidgetHMB.resizeColumnsToContents()
2463
            self.tableWidgetHMB.resizeRowsToContents()
2464

    
2465
        except Exception as ex:
2466
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2467
                      f'{sys.exc_info()[-1].tb_lineno}'
2468
            self.addMessage.emit(MessageType.Error, message)
2469

    
2470
    def load_data(self, drawing):
2471
        """ load data from drawing """
2472
        from AppDocData import AppDocData
2473
        from Calculation import Calculation
2474
        from HydroCalculationCommand import HydroCalculationCommand
2475
        from Drawing import Drawing
2476

    
2477
        try:
2478
            app_doc_data = AppDocData.instance()
2479

    
2480
            self.graphicsView.scene.clear()
2481
            # Set appDocData
2482
            app_doc_data.clear()
2483
            self.onCommandRejected()
2484
            app_doc_data.activeDrawing = drawing
2485

    
2486
            app_doc_data.build_drawing_database(drawing.path)
2487
            self.patch_data()
2488

    
2489
            self.setAttributes(drawing)
2490
            self.setMainWindowTitle(drawing.path)
2491
            self.initTableWidgetHMB()
2492
            self.clear_loop()
2493
            self.clearlogs()
2494
            self.clear_output()
2495
            # Load data on database
2496

    
2497
            self.symbolTreeWidget.initSymbolTreeWidget()
2498

    
2499
            components = app_doc_data.getComponentListByDrawingUID()
2500
            count = len(components)
2501

    
2502
            if count > 0:
2503
                try:
2504
                    self.progress = QProgressDialog(self.tr("Please wait for a while"), self.tr("Cancel"), 0, 100,
2505
                                                    self) if not hasattr(self, 'progress') else self.progress
2506
                    self.progress.setWindowModality(Qt.WindowModal)
2507
                    self.progress.setAutoReset(True)
2508
                    self.progress.setAutoClose(True)
2509
                    self.progress.setMinimum(0)
2510
                    self.progress.resize(600, 100)
2511
                    self.progress.setWindowTitle(self.tr("Load data..."))
2512
                    self.progress.show()
2513

    
2514
                    self.load_components(components)
2515

    
2516
                    """ TODO: 확인 필요
2517
                    # Loops 와 Output 을 만들기 위해 Calculation 을 한다
2518
                    for hmb in app_doc_data.activeDrawing.hmbTable._hmbs:
2519
                        if hmb.phase_type:
2520
                            Calculation(hmb)
2521
                    cmd = HydroCalculationCommand(self.graphicsView)
2522
                    cmd.execute(None)
2523
                    cmd.execute_second(None)
2524

2525
                    app_doc_data.activeDrawing.loops = cmd.loops
2526

2527
                    self.display_loops()
2528
                    self.display_output()
2529

2530
                    self.load_HMB()
2531
                    """
2532
                    self.calculation()
2533
                finally:
2534
                    self.progress.setValue(self.progress.maximum())
2535
                    self.progress.hide()
2536

    
2537
            # self.changeViewCheckedState(False)
2538
            palette = self.graphicsView.palette()
2539
            _pixmap = QPixmap(':/images/error.svg')
2540
            palette.setBrush(QPalette.Background, QBrush(_pixmap))
2541
            self.graphicsView.setPalette(palette)
2542

    
2543
        except Exception as ex:
2544
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2545
                                                           sys.exc_info()[-1].tb_lineno)
2546
            self.addMessage.emit(MessageType.Error, message)
2547

    
2548
    def saveAs_drawing(self, sourceDb):
2549
        import uuid
2550
        from shutil import copyfile
2551
        from datetime import datetime
2552
        from Drawing import Drawing
2553

    
2554
        workspace = self.get_work_space()
2555

    
2556
        options = QFileDialog.Options()
2557
        options |= QFileDialog.DontUseNativeDialog
2558
        name, _ = QFileDialog.getSaveFileName(self, self.tr('Save As'), workspace, 'HYTOS Files (*.hytos)',
2559
                                              options=options)
2560
        if name:
2561
            if os.path.splitext(name)[1] != '.hytos': name += '.hytos'
2562

    
2563
            app_doc_data = AppDocData.instance()
2564
            # copy template.db to name
2565
            copyfile(sourceDb, name)
2566

    
2567
            matches = [drawing for drawing in app_doc_data.getDrawings() if
2568
                       os.path.exists(drawing.path) and os.path.samefile(drawing.path, name)]
2569
            if not matches:
2570
                drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2571
                app_doc_data.saveDrawing(drawing)
2572
            else:
2573
                drawing = Drawing(str(matches[0].UID), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2574
                app_doc_data.updateDrawing(drawing)
2575

    
2576
            self.load_drawing_list()
2577
            self.load_data(drawing)
2578
            self.open_border_file()
2579

    
2580
    '''
2581
        @brief      create new drawing
2582
        @author     yeonjin
2583
        @date       2019.07.03
2584
    '''
2585

    
2586
    def new_drawing(self):
2587
        import uuid
2588
        from shutil import copyfile
2589
        from datetime import datetime
2590
        from Drawing import Drawing
2591

    
2592
        self.save_drawing_if_necessary()
2593

    
2594
        workspace = self.get_work_space()
2595

    
2596
        options = QFileDialog.Options()
2597
        options |= QFileDialog.DontUseNativeDialog
2598
        name, _ = QFileDialog.getSaveFileName(self, self.tr('New'), workspace, 'HYTOS Files (*.hytos)', options=options)
2599
        if name:
2600
            if os.path.splitext(name)[1] != '.hytos': name += '.hytos'
2601

    
2602
            app_doc_data = AppDocData.instance()
2603
            # copy template.db to name
2604
            copyfile(app_doc_data.getTemplateDbPath(), name)
2605

    
2606
            matches = [drawing for drawing in app_doc_data.getDrawings() if
2607
                       os.path.exists(drawing.path) and os.path.samefile(drawing.path, name)]
2608
            if not matches:
2609
                drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2610
                app_doc_data.saveDrawing(drawing)
2611
            else:
2612
                drawing = Drawing(str(matches[0].UID), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2613
                app_doc_data.updateDrawing(drawing)
2614

    
2615
            self.load_drawing_list()
2616
            self.open_border_file()
2617
            self.load_data(drawing)
2618

    
2619
    def on_open_drawing(self):
2620
        """open selected drawing by user"""
2621

    
2622
        self.save_drawing_if_necessary()
2623

    
2624
        workspace = self.get_work_space()
2625
        options = QFileDialog.Options()
2626
        options |= QFileDialog.DontUseNativeDialog
2627
        name, _ = QFileDialog.getOpenFileName(self, self.tr('Open'), workspace, 'HYTOS File(*.hytos)', options=options)
2628
        if name:
2629
            self.open_drawing(name)
2630

    
2631
    def open_drawing(self, name):
2632
        """open given drawing has name"""
2633
        import uuid
2634
        from Drawing import Drawing
2635
        from datetime import datetime
2636

    
2637
        app_doc_data = AppDocData.instance()
2638
        drawings = app_doc_data.getDrawings()
2639
        matches = [drawing for drawing in drawings if os.path.samefile(drawing.path, name)]
2640
        if not matches:
2641
            drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2642
            app_doc_data.saveDrawing(drawing)
2643

    
2644
            self.load_drawing_list()
2645
        else:
2646
            drawing = matches[0]
2647

    
2648
        # disconnect scene changed if signal is connected
2649
        if self.graphicsView.scene.receivers(self.graphicsView.scene.contents_changed) > 0:
2650
            self.graphicsView.scene.contents_changed.disconnect()
2651

    
2652
        self.load_data(drawing)
2653
        self.open_border_file()
2654

    
2655
        # connect scene changed signal
2656
        self.graphicsView.scene.contents_changed.connect(self.scene_changed)
2657

    
2658
    def get_work_space(self):
2659
        """get work space path"""
2660
        app_doc_data = AppDocData.instance()
2661

    
2662
        configs = app_doc_data.getAppConfigs('option', 'WorkSpace')
2663
        if configs and len(configs) == 1:
2664
            return configs[0].value
2665
        else:
2666
            return os.getcwd()
2667

    
2668
    def changeViewCheckedState(self, checked, clear=True):
2669
        '''change view checked state'''
2670
        if clear:
2671
            self.initTableWidgetHMB()
2672
            # self.clear_data()
2673

    
2674
    '''
2675
        @brief      create a line
2676
        @author     humkyung
2677
        @history    Jeongwoo 2018.05.10 Change method for Checkable action
2678
    '''
2679

    
2680
    def onPlaceLine(self):
2681
        self.actionLine.setChecked(True)
2682
        if not hasattr(self.actionLine, 'tag'):
2683
            self.actionLine.tag = PlaceStreamlineCommand.PlaceStreamlineCommand(self.graphicsView)
2684
            self.actionLine.tag.onSuccess.connect(self.on_stream_line_created)
2685
            self.actionLine.tag.onRejected.connect(self.onCommandRejected)
2686

    
2687
        self.graphicsView.command = self.actionLine.tag
2688

    
2689
    def add_new_stream_line(self, stream_line: QEngineeringStreamlineItem) -> None:
2690
        """add a new stream line"""
2691
        self.add_hmb_data(stream_line)
2692
        stream_line.transfer.onRemoved.connect(self.on_item_removed)
2693
        self.load_HMB()
2694

    
2695
    def on_stream_line_created(self):
2696
        """callback after stream line is created"""
2697
        from CreateCommand import CreateCommand
2698
        try:
2699
            count = len(self.actionLine.tag.streamline._vertices)
2700
            if count > 1:
2701
                stream_line = self.actionLine.tag.streamline
2702
                self.add_new_stream_line(stream_line)
2703
                self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [stream_line, ]))
2704
        finally:
2705
            self.actionLine.tag.reset()
2706

    
2707
    def on_stream_line_deleted(self, stream_line):
2708
        """ callback after stream line is deleted """
2709
        app_doc_data = AppDocData.instance()
2710
        activeDrawing = app_doc_data.activeDrawing
2711
        if activeDrawing:
2712
            # activeDrawing.hmbTable.deleteByUID(stream_line.uid)
2713
            activeDrawing.hmbTable.deleteByStreamNo(stream_line.stream_no)
2714

    
2715
        self.load_HMB()
2716

    
2717
    def add_hmb_data(self, stream_line):
2718
        """ add a new hmb data associated with given stream line """
2719
        from HMBTable import HMBTable
2720
        import uuid
2721

    
2722
        try:
2723
            drawing = AppDocData.instance().activeDrawing
2724
            if drawing:
2725
                components_uid = stream_line.uid
2726
                stream_no = self.get_next_stream_no(drawing)
2727
                drawing.hmbTable.add(components_uid, stream_no)
2728
                stream_line.stream_no = stream_no
2729
                stream_line.stream_no_text = str(stream_no)
2730
        except Exception as ex:
2731
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2732
                                                           sys.exc_info()[-1].tb_lineno)
2733
            self.addMessage.emit(MessageType.Error, message)
2734

    
2735
    def get_next_stream_no(self, drawing):
2736
        if len(list(drawing.hmbTable.streamNos())) == 0:
2737
            return 1
2738
        else:
2739
            if len(list(drawing.hmbTable.deleted_stream_nos())) > 0:
2740
                deleted_stream_nos = sorted(list(drawing.hmbTable.deleted_stream_nos()))
2741
                lastStreamNo = deleted_stream_nos[-1]
2742
                return lastStreamNo
2743
            else:
2744
                streamNos = sorted(list(drawing.hmbTable.streamNos()))
2745
                lastStreamNo = streamNos[-1]
2746
                return lastStreamNo + 1
2747

    
2748
    def clear_HMB(self):
2749
        self.tableWidgetHMB.clearContents()
2750
        self.tableWidgetHMB.setColumnCount(0)
2751

    
2752
    def load_HMB(self) -> None:
2753
        """
2754
        display hmb data to table widget
2755
        @return:
2756
        """
2757
        from CalculationValidation import QCalculationValidation
2758

    
2759
        drawing = AppDocData.instance().activeDrawing
2760
        if drawing is None: return
2761

    
2762
        try:
2763
            hmbs = drawing.hmbTable._hmbs
2764
            if hmbs is not None:
2765
                model = self.tableWidgetHMB.model()
2766
                model.setColumnCount(2)
2767
                # self.tableWidgetHMB.setColumnCount(2)
2768

    
2769
                col_names = ['Stream No.\n', 'Unit\n']
2770
                _hmbs = sorted(hmbs, key=lambda hmb: hmb.stream_no)
2771
                for hmb in _hmbs:
2772
                    columnCount = model.columnCount()
2773
                    # self.tableWidgetHMB.setColumnCount(columnCount + 1)
2774
                    col_names.append(str(hmb.stream_no) + '\n(' + str(hmb.stream_no_text if hmb.stream_no_text else hmb._stream_no) + ')')
2775

    
2776
                    item = set_item_properties(hmb.uid, Qt.AlignHCenter | Qt.AlignVCenter)
2777
                    model.setItem(0, columnCount, item)
2778
                    item = set_item_properties(hmb.components_uid, Qt.AlignHCenter | Qt.AlignVCenter)
2779
                    model.setItem(1, columnCount, item)
2780
                    item = set_item_properties(hmb.stream_no, Qt.AlignHCenter | Qt.AlignVCenter)
2781
                    model.setItem(2, columnCount, item)
2782
                    item = set_item_properties(hmb.phase_type, Qt.AlignHCenter | Qt.AlignVCenter)
2783
                    item.setData(hmb.phase_type, Qt.UserRole)
2784
                    model.setItem(3, columnCount, item)
2785

    
2786
                    item = set_item_properties(convert_to_fixed_point(hmb.flowrate_mass), Qt.AlignHCenter | Qt.AlignVCenter)
2787
                    item.setData(hmb.flowrate_mass, Qt.UserRole)
2788
                    model.setItem(4, columnCount, item)
2789

    
2790
                    item = set_item_properties(convert_to_fixed_point(hmb.flowrate_volume), Qt.AlignHCenter | Qt.AlignVCenter)
2791
                    item.setData(hmb.flowrate_volume, Qt.UserRole)
2792
                    """
2793
                    if hmb.phase_type == 'Vapor':
2794
                        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
2795
                    """
2796
                    model.setItem(5, columnCount, item)
2797

    
2798
                    item = set_item_properties(convert_to_fixed_point(hmb.density), Qt.AlignHCenter | Qt.AlignVCenter)
2799
                    item.setData(hmb.density, Qt.UserRole)
2800
                    model.setItem(6, columnCount, item)
2801

    
2802
                    item = set_item_properties(convert_to_fixed_point(hmb.viscosity), Qt.AlignHCenter | Qt.AlignVCenter)
2803
                    item.setData(hmb.viscosity, Qt.UserRole)
2804
                    model.setItem(7, columnCount, item)
2805

    
2806
                    item = set_item_properties(convert_to_fixed_point(hmb.temperature), Qt.AlignHCenter | Qt.AlignVCenter)
2807
                    item.setData(hmb.temperature, Qt.UserRole)
2808
                    model.setItem(8, columnCount, item)
2809

    
2810
                    item = set_item_properties(convert_to_fixed_point(hmb.molecular_weight), Qt.AlignHCenter | Qt.AlignVCenter)
2811
                    item.setData(hmb.molecular_weight, Qt.UserRole)
2812
                    model.setItem(9, columnCount, item)
2813

    
2814
                    item = set_item_properties(convert_to_fixed_point(hmb.specific_heat_ratio), Qt.AlignHCenter | Qt.AlignVCenter)
2815
                    item.setData(hmb.specific_heat_ratio, Qt.UserRole)
2816
                    model.setItem(10, columnCount, item)
2817

    
2818
                    item = set_item_properties(convert_to_fixed_point(hmb.compress_factor), Qt.AlignHCenter | Qt.AlignVCenter)
2819
                    item.setData(hmb.compress_factor, Qt.UserRole)
2820
                    model.setItem(11, columnCount, item)
2821

    
2822
                    item = set_item_properties(convert_to_fixed_point(hmb.nominal_pipe_size), Qt.AlignHCenter | Qt.AlignVCenter)
2823
                    item.setData(hmb.nominal_pipe_size, Qt.UserRole)
2824
                    model.setItem(12, columnCount, item)
2825

    
2826
                    item = set_item_properties(convert_to_fixed_point(hmb.inside_pipe_size), Qt.AlignHCenter | Qt.AlignVCenter)
2827
                    item.setData(hmb.inside_pipe_size, Qt.UserRole)
2828
                    model.setItem(13, columnCount, item)
2829

    
2830
                    item = set_item_properties(convert_to_fixed_point(hmb.schedule_no), Qt.AlignHCenter | Qt.AlignVCenter)
2831
                    item.setData(hmb.schedule_no, Qt.UserRole)
2832
                    model.setItem(14, columnCount, item)
2833

    
2834
                    item = set_item_properties(convert_to_fixed_point(hmb.straight_length), Qt.AlignHCenter | Qt.AlignVCenter)
2835
                    item.setData(hmb.straight_length, Qt.UserRole)
2836
                    model.setItem(15, columnCount, item)
2837

    
2838
                    item = set_item_properties(convert_to_fixed_point(hmb.equivalent_length), Qt.AlignHCenter | Qt.AlignVCenter)
2839
                    item.setData(hmb.equivalent_length, Qt.UserRole)
2840
                    model.setItem(16, columnCount, item)
2841

    
2842
                    item = set_item_properties(convert_to_fixed_point(hmb.equivalent_length_input), Qt.AlignHCenter | Qt.AlignVCenter)
2843
                    item.setData(hmb.equivalent_length_input, Qt.UserRole)
2844
                    model.setItem(17, columnCount, item)
2845

    
2846
                    item = set_item_properties(convert_to_fixed_point(hmb.fitting_length), Qt.AlignHCenter | Qt.AlignVCenter)
2847
                    item.setData(hmb.fitting_length, Qt.UserRole)
2848
                    model.setItem(18, columnCount, item)
2849

    
2850
                    item = set_item_properties(convert_to_fixed_point(hmb.fitting_K), Qt.AlignHCenter | Qt.AlignVCenter)
2851
                    item.setData(hmb.fitting_K, Qt.UserRole)
2852
                    model.setItem(19, columnCount, item)
2853

    
2854
                    item = set_item_properties(convert_to_fixed_point(hmb.equivalent_length_cal), Qt.AlignHCenter | Qt.AlignVCenter)
2855
                    item.setData(hmb.equivalent_length_cal, Qt.UserRole)
2856
                    model.setItem(20, columnCount, item)
2857

    
2858
                    item = set_item_properties(convert_to_fixed_point(hmb.roughness), Qt.AlignHCenter | Qt.AlignVCenter)
2859
                    item.setData(hmb.roughness, Qt.UserRole)
2860
                    model.setItem(21, columnCount, item)
2861

    
2862
                    item = set_item_properties(convert_to_fixed_point(hmb.limitation_velocity), Qt.AlignHCenter | Qt.AlignVCenter)
2863
                    item.setData(hmb.limitation_velocity, Qt.UserRole)
2864
                    model.setItem(22, columnCount, item)
2865

    
2866
                    item = set_item_properties(convert_to_fixed_point(hmb.limitation_pressure_drop), Qt.AlignHCenter | Qt.AlignVCenter)
2867
                    item.setData(hmb.limitation_pressure_drop, Qt.UserRole)
2868
                    model.setItem(23, columnCount, item)
2869

    
2870
                    item = set_item_properties(None, Qt.AlignHCenter | Qt.AlignVCenter, QColor(153, 204, 255))
2871
                    model.setItem(24, columnCount, item)
2872

    
2873
                    item = set_item_properties(convert_to_fixed_point(hmb.velocity), Qt.AlignHCenter | Qt.AlignVCenter)
2874
                    item.setEditable(False)
2875
                    model.setItem(25, columnCount, item)
2876

    
2877
                    item = set_item_properties(convert_to_fixed_point(hmb.reynolds), Qt.AlignHCenter | Qt.AlignVCenter)
2878
                    item.setEditable(False)
2879
                    model.setItem(26, columnCount, item)
2880

    
2881
                    item = set_item_properties(convert_to_fixed_point(hmb.friction_factor), Qt.AlignHCenter | Qt.AlignVCenter)
2882
                    item.setEditable(False)
2883
                    model.setItem(27, columnCount, item)
2884

    
2885
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_drop), Qt.AlignHCenter | Qt.AlignVCenter)
2886
                    item.setEditable(False)
2887
                    model.setItem(28, columnCount, item)
2888

    
2889
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_drop_friction), Qt.AlignHCenter | Qt.AlignVCenter)
2890
                    item.setEditable(False)
2891
                    model.setItem(29, columnCount, item)
2892

    
2893
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_drop_static), Qt.AlignHCenter | Qt.AlignVCenter)
2894
                    item.setEditable(False)
2895
                    model.setItem(30, columnCount, item)
2896

    
2897
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_pipe_end_point), Qt.AlignHCenter | Qt.AlignVCenter)
2898
                    item.setEditable(False)
2899
                    model.setItem(31, columnCount, item)
2900

    
2901
                    item = set_item_properties(convert_to_fixed_point(hmb.power), Qt.AlignHCenter | Qt.AlignVCenter)
2902
                    item.setEditable(False)
2903
                    model.setItem(32, columnCount, item)
2904

    
2905
                    if hmb.isDeleted:
2906
                        self.tableWidgetHMB.hideColumn(columnCount)
2907

    
2908
                    # mark if velocity or pressure drop is over criteria value
2909
                    if hmb.is_over_criteria:
2910
                        rows = model.rowCount()
2911
                        for row in range(rows):
2912
                            item = model.item(row, columnCount)
2913
                            item.setBackground(Qt.red)
2914
                    # up to here
2915

    
2916
                model.setHorizontalHeaderLabels(col_names)
2917
                self.tableWidgetHMB.setModel(model)
2918

    
2919
                self.tableWidgetHMB.resizeColumnsToContents()
2920
                self.tableWidgetHMB.resizeRowsToContents()
2921
        except Exception as ex:
2922
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2923
                      f"{sys.exc_info()[-1].tb_lineno}"
2924
            self.addMessage.emit(MessageType.Error, message)
2925

    
2926
    '''
2927
        @brief      refresh scene
2928
        @author     humkyung
2929
        @date       2018.07.23
2930
    '''
2931

    
2932
    def onCommandRejected(self, cmd=None):
2933
        try:
2934
            if type(cmd) is PlaceStreamlineCommand.PlaceStreamlineCommand:
2935
                if self.actionLine.tag.streamline:
2936
                    self.graphicsView.scene.removeItem(self.actionLine.tag.streamline)
2937
                self.graphicsView.scene.update()
2938
                self.actionLine.tag.reset()
2939

    
2940
                self.actionLine.setChecked(False)
2941
            elif type(cmd) is AreaZoomCommand.AreaZoomCommand:
2942
                self.actionZoom.setChecked(False)
2943
            else:
2944
                if hasattr(self.actionLine, 'tag') and self.actionLine.tag.streamline:
2945
                    self.graphicsView.scene.removeItem(self.actionLine.tag.streamline)
2946
                    self.graphicsView.scene.update()
2947
                    self.actionLine.tag.reset()
2948

    
2949
                self.actionLine.setChecked(False)
2950
                self.actionZoom.setChecked(False)
2951
        finally:
2952
            self.graphicsView.useDefaultCommand()
2953

    
2954
    '''
2955
        @brief      restore to default command when user press Escape key
2956
        @author     humkyung 
2957
        @date       2018.08.09
2958
        
2959
    '''
2960
    def keyPressEvent(self, event):
2961
        try:
2962
            if event.key() == Qt.Key_Escape:
2963
                self.graphicsView.useDefaultCommand()
2964
            elif (event.key() == Qt.Key_C) and (event.modifiers() & Qt.ControlModifier):
2965
                if self.tableWidgetHMB.hasFocus():
2966
                    self.copy_selection(self.tableWidgetHMB)
2967
            elif (event.key() == Qt.Key_V) and (event.modifiers() & Qt.ControlModifier):
2968
                if self.tableWidgetHMB.hasFocus():
2969
                    self.paste_selection(self.tableWidgetHMB)
2970

    
2971
            QMainWindow.keyPressEvent(self, event)
2972
        except Exception as ex:
2973
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2974
                      f'{sys.exc_info()[-1].tb_lineno}'
2975
            self.addMessage.emit(MessageType.Error, message)
2976

    
2977
    def on_item_removed(self, item):
2978
        """remove item from tree widget and then remove from scene"""
2979

    
2980
        try:
2981
            matches = [_item for _item in self.graphicsView.scene.items() if hasattr(_item, 'connectors') and \
2982
                       [connector for connector in _item.connectors if
2983
                        connector.connectedItem is not None and connector.connectedItem.parentItem() is item]]
2984
            # disconnect deleted item
2985
            for match in matches:
2986
                for connector in match.connectors:
2987
                    if connector.connectedItem and connector.connectedItem.parentItem() is item:
2988
                        connector.connect(None)
2989

    
2990
            matches = [_item for _item in self.graphicsView.scene.items() if hasattr(_item, 'owner')]
2991
            for match in matches:
2992
                if match.owner is item:
2993
                    match.owner = None
2994

    
2995
            matches = [_item for _item in self.graphicsView.scene.items() if hasattr(_item, 'attrs')]
2996
            done = False
2997
            for match in matches:
2998
                for assoc in match.associations():
2999
                    if item is assoc:
3000
                        match.remove_assoc_item(item)
3001
                        for attr in match.attrs.keys():
3002
                            if str(item.uid) == str(attr.AssocItem.uid):
3003
                                attr.AssocItem = None
3004
                                match.attrs[attr] = ''
3005
                                done = True
3006
                                break
3007
                        break
3008
                if done: break
3009

    
3010
            if type(item) is QEngineeringStreamlineItem:
3011
                self.on_stream_line_deleted(item)
3012

    
3013
            if item.scene() is not None:
3014
                item.scene().removeItem(item)
3015
                del item
3016
        except Exception as ex:
3017
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
3018
                                                           sys.exc_info()[-1].tb_lineno)
3019
            self.addMessage.emit(MessageType.Error, message)
3020

    
3021
    '''
3022
        @brief      load components
3023
        @author     yeonjin
3024
        @date       2019.07.30.
3025
    '''
3026

    
3027
    def load_components(self, componentsUID):
3028
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
3029
        from EngineeringCalloutTextItem import QEngineeringCalloutTextItem
3030
        from EngineeringDimensionItem import QEngineeringDimensionItem
3031
        from EngineeringCloudItem import QEngineeringCloudItem
3032

    
3033
        try:
3034
            app_doc_data = AppDocData.instance()
3035

    
3036
            maxValue = len(componentsUID)
3037
            self.progress.setMaximum(maxValue)
3038

    
3039
            for componentUID in componentsUID:
3040
                componentInfos = app_doc_data.getComponentByComponentUID(componentUID)
3041
                if (len(componentInfos)) > 0:
3042
                    category = componentInfos[0]['Category']  # Category@SymbolType
3043
                    symbol_name = componentInfos[0]['Symbol_Name']  # Name@Symbols
3044

    
3045
                    if category == 'Stream Line':
3046
                        item = QEngineeringStreamlineItem.fromDatabase(componentInfos)
3047
                        if item is not None:
3048
                            item.transfer.onRemoved.connect(self.on_item_removed)
3049
                            self.graphicsView.scene.addItem(item)
3050
                    elif symbol_name == 'Callout':
3051
                        item = QEngineeringCalloutTextItem.fromDatabase(componentInfos)
3052
                        if item:
3053
                            item.transfer.onRemoved.connect(self.on_item_removed)
3054
                            item.lost_focus.connect(self.editor_lost_focus)
3055
                            item.selected_change.connect(self.item_selected)
3056
                            self.graphicsView.scene.addItem(item)
3057
                    elif symbol_name == 'Dimension':
3058
                        item = QEngineeringDimensionItem.fromDatabase(componentInfos)
3059
                        if item:
3060
                            item.transfer.onRemoved.connect(self.on_item_removed)
3061
                            self.graphicsView.scene.addItem(item)
3062
                    elif symbol_name == 'Cloud':
3063
                        item = QEngineeringCloudItem.fromDatabase(componentInfos)
3064
                        if item:
3065
                            self.graphicsView.scene.addItem(item)
3066
                    else:
3067
                        item = SymbolSvgItem.fromDatabase(componentInfos)
3068
                        if item is not None:
3069
                            item.transfer.onRemoved.connect(self.on_item_removed)
3070
                            app_doc_data.symbols.append(item)
3071
                            item.addSvgItemToScene(self.graphicsView.scene)
3072

    
3073
                    self.progress.setValue(self.progress.value() + 1)
3074

    
3075
                QApplication.processEvents()
3076

    
3077
            #self.rebuild_label()
3078

    
3079
            # """ update scene """
3080
            self.graphicsView.scene.update(self.graphicsView.sceneRect())
3081
            for item in self.graphicsView.scene.items():
3082
                item.setVisible(True)
3083

    
3084
        except Exception as ex:
3085
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
3086
                                                           sys.exc_info()[-1].tb_lineno)
3087
            self.addMessage.emit(MessageType.Error, message)
3088
        finally:
3089
            pass
3090

    
3091
    '''
3092
        @brief      Remove added item on same place and Add GraphicsItem
3093
        @author     Jeongwoo
3094
        @date       2018.05.25
3095
        @history    2018.05.29  Jeongwoo    Moved from QRecognitionDialog
3096
                    2018.06.05  Jeongwoo    Remove Size condition
3097
                    2018.06.18  Jeongwoo    Set Z-index
3098
    '''
3099

    
3100
    def addTextItemToScene(self, textItem):
3101
        textItem.addTextItemToScene(self.graphicsView.scene)
3102

    
3103
    '''
3104
        @brief      Remove added item on same place and Add GraphicsItem
3105
        @author     Jeongwoo
3106
        @date       2018.05.29
3107
        @history    2018.06.18  Jeongwoo    Set Z-index
3108
    '''
3109

    
3110
    def addLineItemToScene(self, lineItem):
3111
        self.graphicsView.scene.addItem(lineItem)
3112

    
3113
    '''
3114
        @brief      Check Number
3115
        @author     kyouho
3116
        @date       2018.08.20
3117
    '''
3118

    
3119
    def isNumber(self, num):
3120
        p = re.compile('(^[0-9]+$)')
3121
        result = p.match(num)
3122

    
3123
        if result:
3124
            return True
3125
        else:
3126
            return False
3127

    
3128
    '''
3129
        @brief      find overlap Connector
3130
        @author     kyouho
3131
        @date       2018.08.28
3132
    '''
3133

    
3134
    def findOverlapConnector(self, connectorItem):
3135
        from shapely.geometry import Point
3136
        from EngineeringConnectorItem import QEngineeringConnectorItem
3137
        itemList = []
3138

    
3139
        x = connectorItem.center()[0]
3140
        y = connectorItem.center()[1]
3141

    
3142
        connectors = [item for item in self.graphicsView.scene.items() if
3143
                      type(item) is QEngineeringConnectorItem and item != connectorItem]
3144
        for connector in connectors:
3145
            if Point(x, y).distance(Point(connector.center()[0], connector.center()[1])) < 5:
3146
                itemList.append(connector.parent)
3147

    
3148
        return itemList
3149

    
3150
    def add_data(self, table_widget, name, value, unit, separation=None, is_cell_merge=None, foregroundcolor=None):
3151
        def make_table_widget_item(name: str, alignment, backgroundcolor=None, foregroundcolor=None) -> QTableWidgetItem:
3152
            if name is None:
3153
                name = ''
3154

    
3155
            item = QTableWidgetItem(str(name))
3156
            item.setTextAlignment(alignment)
3157
            if backgroundcolor:
3158
                item.setBackground(backgroundcolor)
3159
            if foregroundcolor:
3160
                item.setForeground(foregroundcolor)  # QBrush(QColor(255, 0, 0)))
3161
            return item
3162

    
3163
        row = table_widget.rowCount()
3164
        table_widget.setRowCount(row + 1)
3165

    
3166
        if separation:
3167
            if is_cell_merge:
3168
                table_widget.setSpan(row, 0, 1, 3)
3169
                table_widget.setItem(row, 0, make_table_widget_item(name, Qt.AlignLeft | Qt.AlignVCenter,
3170
                                                                    QColor(153, 204, 255), foregroundcolor))
3171
            else:
3172
                table_widget.setItem(row, 0, make_table_widget_item(name, Qt.AlignLeft | Qt.AlignVCenter,
3173
                                                                    QColor(153, 204, 255), foregroundcolor))
3174
                table_widget.setItem(row, 1, make_table_widget_item(value, Qt.AlignHCenter | Qt.AlignVCenter,
3175
                                                                    QColor(153, 204, 255), foregroundcolor))
3176
                table_widget.setItem(row, 2, make_table_widget_item(unit, Qt.AlignHCenter | Qt.AlignVCenter,
3177
                                                                    QColor(153, 204, 255), foregroundcolor))
3178
        else:
3179
            if is_cell_merge:
3180
                table_widget.setSpan(row, 0, 1, 3)
3181
                table_widget.setItem(row, 0, make_table_widget_item(name, Qt.AlignLeft | Qt.AlignVCenter,
3182
                                                                    None, foregroundcolor))
3183
            else:
3184
                table_widget.setItem(row, 0, make_table_widget_item(name, Qt.AlignLeft | Qt.AlignVCenter,
3185
                                                                    None, foregroundcolor))
3186
                table_widget.setItem(row, 1, make_table_widget_item(value, Qt.AlignRight | Qt.AlignVCenter,
3187
                                                                    None, foregroundcolor))
3188
                table_widget.setItem(row, 2, make_table_widget_item(unit, Qt.AlignLeft | Qt.AlignVCenter,
3189
                                                                    None, foregroundcolor))
3190

    
3191
        table_widget.resizeRowsToContents()
3192
        table_widget.resizeColumnsToContents()
3193

    
3194

    
3195
if __name__ == '__main__':
3196
    pass
클립보드 이미지 추가 (최대 크기: 500 MB)