프로젝트

일반

사용자정보

통계
| 개정판:

hytos / HYTOS / HYTOS / MainWindow.py @ 16ad12a7

이력 | 보기 | 이력해설 | 다운로드 (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
    item = QTableWidgetItem(str(name))
72
    item.setTextAlignment(alignment)
73
    if backgroundcolor:
74
        item.setBackground(backgroundcolor)
75
    if foregroundcolor:
76
        item.setForeground(foregroundcolor)  # QBrush(QColor(255, 0, 0)))
77
    return item
78
    """
79

    
80

    
81
def convert_to_fixed_point(value):
82
    if is_float(str(value)):
83
        tokens = f"{float(value):.10f}".split('.')
84
        if len(tokens) == 2:
85
            # 소수점 아래가 있을 경우 소수점 아래의 trailing zero를 제거한다.
86
            if is_blank(tokens[1].rstrip('0')):
87
                return tokens[0]
88
            else:
89
                tokens[1] = tokens[1].rstrip('0')
90
                return '.'.join(tokens)
91
        else:
92
            return tokens[0]
93
    else:
94
        return value
95

    
96

    
97
class ItemDelegate(QStyledItemDelegate):
98
    def createEditor(self, parent, option, index):
99
        """
100
        create a editor for user input
101
        @param parent:
102
        @param option:
103
        @param index:
104
        @return:
105
        """
106
        editor = None
107
        if index.column() >= 2:
108
            if index.row() == 3:
109
                editor = QComboBox(parent)
110
                editor.addItems(['Liquid', 'Vapor', 'Mixed'])
111
            elif index.row() <= 23:
112
                editor = QLineEdit(parent)
113
                if index.row() in [4, 5]:
114
                    validator = QDoubleValidator(parent)
115
                    editor.setValidator(validator)
116

    
117
                value = editor.text()
118
                if type(value) is float:
119
                    validator = QDoubleValidator(parent)
120
                    editor.setValidator(validator)
121

    
122
        return editor
123

    
124
    def destroyEditor(self, editor, index):
125
        """
126
        change background if value is changed
127
        @param editor:
128
        @param index:
129
        @return:
130
        """
131
        editor.blockSignals(True)
132
        if type(editor) is QComboBox:
133
            changed = editor.currentText()
134
        else:
135
            changed = editor.text()
136
        item = editor.parentWidget().parentWidget().model().item(index.row(), index.column())
137
        value = item.data(Qt.UserRole)
138

    
139
        value_changed = False
140
        if type(value) is float and changed:
141
            value_changed = float(changed) != value
142
            item.setBackground(Qt.magenta if value_changed else Qt.white)
143
        else:
144
            value_changed = changed != value
145
            item.setBackground(Qt.magenta if value_changed else Qt.white)
146

    
147
        """clear value of other side"""
148
        if value_changed:
149
            if index.row() == 4:
150
                item = editor.parentWidget().parentWidget().model().item(5, index.column())
151
                item.setText('')
152
                item.setBackground(Qt.magenta)
153
            elif index.row() == 5:
154
                item = editor.parentWidget().parentWidget().model().item(4, index.column())
155
                item.setText('')
156
                item.setBackground(Qt.magenta)
157

    
158
        """
159
        if index.row() == 3:
160
            item = editor.parentWidget().parentWidget().item(5, index.column())
161
            if changed in ['Liquid', 'Mixed']:
162
                item.setFlags(item.flags() | Qt.ItemIsEditable)
163
            else:
164
                item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
165
        """
166

    
167
        editor.blockSignals(False)
168

    
169

    
170
class MainWindow(QMainWindow, MainWindow_UI.Ui_MainWindow, SingletonInstane):
171
    """ This is MainWindow class """
172
    DEFAULT_SIZE = QSizeF(1188, 841)
173
    addMessage = pyqtSignal(Enum, str)
174

    
175
    def __init__(self):
176
        """initialize"""
177

    
178
        try:
179
            super(self.__class__, self).__init__()
180
            self.setupUi(self)
181
            self.tableWidgetHMB = FreezeTableWidget(self, 2)
182
            self.horizontalLayoutStreamData.addWidget(self.tableWidgetHMB)
183

    
184
            self._label_mouse = QLabel(self.statusbar)
185
            self._label_mouse.setText(self.tr(f'mouse pos : ({0},{0})'))
186
            self.statusbar.addWidget(self._label_mouse)
187
            self.addMessage.connect(self.onAddMessage)
188

    
189
            self.setMainWindowTitle()
190

    
191
            self.graphicsView = QtImageViewer.QtImageViewer(self)
192
            self.graphicsView.setParent(self.centralwidget)
193
            self.graphicsView.useDefaultCommand()
194
            self.graphicsView.setMouseTracking(True)
195
            self.graphicsView.viewport().installEventFilter(self)
196
            self.verticalLayout.addWidget(self.graphicsView)
197

    
198
            # Add Symbol TreeWidget
199
            self.symbolTreeWidget = SymbolTreeWidget.QSymbolTreeWidget()
200
            self.symbolTreeWidget.header().hide()
201
            self.verticalLayoutSymbolList.addWidget(self.symbolTreeWidget)
202

    
203
            # Initialize Action group
204
            self.actionGroup = QActionGroup(self)
205
            self.actionGroup.addAction(self.actionLine)
206
            self.actionGroup.addAction(self.actionZoom)
207
            self.actionGroup.addAction(self.actionFitWindow)
208
            self.actionGroup.addAction(self.actionSave)
209

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

    
238
            self.actionLeftAlignment.triggered.connect(self.on_left_alignment)
239
            self.actionRightAlignment.triggered.connect(self.on_right_alignment)
240
            self.actionUpAlignment.triggered.connect(self.on_up_alignment)
241
            self.actionDownAlignment.triggered.connect(self.on_down_alignment)
242

    
243
            self.treeViewDrawingList.setContextMenuPolicy(Qt.CustomContextMenu)
244
            self.treeViewDrawingList.customContextMenuRequested.connect(self.open_context_menu)
245
            self.treeViewDrawingList.doubleClicked.connect(self.open_selected_drawing)
246

    
247
            delegate = ItemDelegate(self.tableWidgetHMB)
248
            self.tableWidgetHMB.setItemDelegate(delegate)
249
            self.toolButtonSyncStreamData.clicked.connect(self.on_sync_stream_data)
250
            self.toolButtonRevertStreamData.clicked.connect(self.load_HMB)
251
            self.initTableWidgetHMB()
252

    
253
            self.load_drawing_list()
254
            self.load_stylesheet_file()
255
            self.load_language_file()
256

    
257
            self.tabifyDockWidget(self.dockWidgetDrawingExplorer, self.dockWidgetSymbolExplorer)
258
            self.dockWidgetDrawingExplorer.raise_()
259

    
260
            self.read_settings()
261

    
262
            # ToDo..
263
            # Menu bar - Admin Hide
264
            # Loop Tab Hide
265
        except Exception as ex:
266
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
267
                      f"{sys.exc_info()[-1].tb_lineno}"
268
            self.addMessage.emit(MessageType.Error, message)
269

    
270
    def read_settings(self):
271
        """read geometry and state"""
272
        from App import App
273

    
274
        try:
275
            self.settings = QSettings(App.COMPANY, App.NAME)
276
            self.restoreGeometry(self.settings.value("geometry", ""))
277
            self.restoreState(self.settings.value("windowState", ""))
278
        except Exception as ex:
279
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
280
                                                           sys.exc_info()[-1].tb_lineno)
281
            self.addMessage.emit(MessageType.Error, message)
282

    
283
    def load_stylesheet_file(self):
284
        # load stylesheet file list
285
        stylesheet_name = QtWidgets.qApp.stylesheet_name
286
        files = [os.path.splitext(file)[0] for file in os.listdir(os.path.dirname(os.path.realpath(__file__))) if
287
                 os.path.splitext(file)[1] == '.qss']
288
        for file in files:
289
            action = self.menuTheme.addAction(file)
290
            action.setCheckable(True)
291
            action.setChecked(True) if stylesheet_name == file else action.setChecked(False)
292
            action.triggered.connect(partial(self.load_stylesheet, file))
293
        # up to here
294

    
295
    def load_language_file(self):
296
        # load language files
297
        language_name = QtWidgets.qApp.language_name
298
        files = ['en_us']  # englisgh is default language
299
        files.extend([os.path.splitext(file)[0] for file in
300
                      os.listdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'translate')) if
301
                      os.path.splitext(file)[1] == '.qm'])
302
        for file in files:
303
            action = self.menuLanguage.addAction(file)
304
            action.setCheckable(True)
305
            action.setChecked(True) if language_name.lower() == file.lower() else action.setChecked(False)
306
            action.triggered.connect(partial(self.load_language, file))
307
        # up to here
308

    
309
    def setMainWindowTitle(self, drawingName=None):
310
        try:
311
            _translate = QCoreApplication.translate
312

    
313
            version = QCoreApplication.applicationVersion()
314
            if drawingName is None:
315
                self.setWindowTitle(_translate(App.NAME + f"({version})", App.NAME + f"({version})"))
316
            else:
317
                self.setWindowTitle(
318
                    _translate(App.NAME + f"({version})", App.NAME + f"({version})") + f" - {drawingName}")
319
        except Exception as ex:
320
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
321
                      f"{sys.exc_info()[-1].tb_lineno}"
322
            self.addMessage.emit(MessageType.Error, message)
323

    
324
    def on_sync_stream_data(self):
325
        """
326
        sync stream data
327
        @return: None
328
        """
329

    
330
        drawing = AppDocData.instance().activeDrawing
331
        if drawing is None:
332
            return
333

    
334
        try:
335
            model = self.tableWidgetHMB.model()
336
            for col in range(2, model.columnCount()):
337
                hmb = None
338
                if self.tableWidgetHMB.isColumnHidden(col):
339
                    continue
340

    
341
                components_uid = model.item(1, col).text()
342
                hmb = drawing.hmbTable.get_hmb_data(components_uid)
343

    
344
                for row in range(2, 23):
345
                    item = model.item(row, col)
346
                    if Qt.magenta != item.background():
347
                        continue
348

    
349
                    value = item.text()
350
                    origin_value = item.data(Qt.UserRole)
351
                    """try to convert data type"""
352
                    value = float(value) if value and type(origin_value) is float else value
353

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

    
406
    def clearlogs(self):
407
        """
408
        @brief: clear logs
409
        @return: None
410
        """
411
        try:
412
            self.listWidgetLogs.clear()
413
        except Exception as ex:
414
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
415
                      f'{sys.exc_info()[-1].tb_lineno}'
416
            self.addMessage.emit(MessageType.Error, message)
417
        finally:
418
            logs = self.listWidgetLogs.count()
419
            if logs:
420
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs({})'.format(logs))
421
            else:
422
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs')
423

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

    
427
        self.settings.setValue('geometry', self.saveGeometry())
428
        self.settings.setValue('windowState', self.saveState())
429

    
430
        self.save_drawing_if_necessary()
431

    
432
        """update view region"""
433
        app_doc_data = AppDocData.instance()
434
        if app_doc_data.activeDrawing:
435
            rect = self.graphicsView.viewport().rect()
436
            app_doc_data.activeDrawing.view_rect = self.graphicsView.mapToScene(rect).boundingRect()
437
            app_doc_data.update_view_region(app_doc_data.activeDrawing)
438
        """up to here"""
439

    
440
        event.accept()
441

    
442
    def open_context_menu(self, position):
443
        indexes = self.treeViewDrawingList.selectedIndexes()
444
        if len(indexes) > 0:
445
            index = indexes[0]
446
            item = index.model().itemFromIndex(index)
447

    
448
            menu = QMenu()
449

    
450
            if not item.parent():
451
                newDrawingAction = menu.addAction(QIcon(':/images/New.svg'), self.tr('New...'))
452
                newDrawingAction.triggered.connect(lambda: self.newDrawingActionClickEvent(item))
453
                menu.addAction(newDrawingAction)
454
            else:
455
                deleteDrawingAction = menu.addAction(QIcon(':/images/Cancel.svg'), self.tr("Delete"))
456
                deleteDrawingAction.triggered.connect(lambda: self.deleteDrawingActionClickEvent(item))
457
                menu.addAction(deleteDrawingAction)
458

    
459
            menu.exec_(self.treeViewDrawingList.viewport().mapToGlobal(position))
460

    
461
    def on_new_drawing(self):
462
        self.new_drawing()
463

    
464
    def newDrawingActionClickEvent(self, item):
465
        self.new_drawing()
466

    
467
    def saveAsDrawingActionClickEvent(self, item):
468
        sourceDb = item.text(2)
469

    
470
        self.saveAs_drawing(sourceDb)
471

    
472
    def deleteDrawingActionClickEvent(self, item):
473
        """ delete selected drawing """
474
        try:
475
            msg = QMessageBox(self)
476
            msg.setIcon(QMessageBox.Question)
477
            msg.setText(self.tr('Do you want to delete drawing on list?\nHowever, Actual drawing is not deleted.'))
478
            msg.setWindowTitle(self.tr("Delete"))
479
            msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
480

    
481
            if QMessageBox.Ok == msg.exec_():
482
                model = self.treeViewDrawingList.model()
483
                index = item.index()
484
                drawing_name = model.data(model.index(index.row(), 0, index.parent()))
485
                drawing_path = model.data(model.index(index.row(), 2, index.parent()))
486

    
487
                app_doc_data = AppDocData.instance()
488
                app_doc_data.deleteDrawingByName(drawing_path)
489

    
490
                if app_doc_data.activeDrawing:
491
                    if drawing_name == app_doc_data.activeDrawing.name and drawing_path == app_doc_data.activeDrawing.path:
492
                        app_doc_data.activeDrawing = None
493

    
494
                        if self.graphicsView.hasImage():
495
                            self.graphicsView.clearImage()
496
                            self.graphicsView.scene.clear()
497

    
498
                        self.initTableWidgetHMB()
499

    
500
                self.load_drawing_list()
501
        except Exception as ex:
502
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
503
                      f"{sys.exc_info()[-1].tb_lineno}"
504
            self.addMessage.emit(MessageType.Error, message)
505

    
506
    def clear_data(self):
507
        self.clear_HMB()
508

    
509
    def eventFilter(self, source, event):
510
        """ display mouse position of graphics view """
511
        if event.type() == QEvent.MouseMove:
512
            pos = self.graphicsView.mapToScene(event.pos())
513
            self._label_mouse.setText('mouse pos : ({},{})'.format(round(pos.x()), round(pos.y())))
514

    
515
        return QWidget.eventFilter(self, source, event)
516

    
517
    def load_stylesheet(self, file):
518
        """load stylesheets"""
519

    
520
        QtWidgets.qApp.loadStyleSheet(os.path.join(os.path.dirname(os.path.realpath(__file__)), file))
521

    
522
        app_doc_data = AppDocData.instance()
523
        configs = [Config('app', 'stylesheet', file)]
524
        app_doc_data.saveAppConfigs(configs)
525

    
526
        for action in self.menuTheme.actions():
527
            if action.text() == file: continue
528
            action.setChecked(False)
529

    
530
    def load_language(self, file):
531
        """ load language file and then apply selected language """
532
        try:
533

    
534
            qm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'translate', '{0}.qm'.format(file))
535
            QtWidgets.qApp.load_language(qm_file)
536

    
537
            app_doc_data = AppDocData.instance()
538
            configs = [Config('app', 'language', file)]
539
            app_doc_data.saveAppConfigs(configs)
540

    
541
            for action in self.menuLanguage.actions():
542
                if action.text().lower() == file.lower(): continue
543
                action.setChecked(False)
544
        finally:
545
            self.retranslateUi(self)
546

    
547
    def clear_output(self):
548
        self.tableWidgetOutput.setColumnCount(0)
549
        self.tableWidgetOutput.setRowCount(0)
550

    
551
        self.tableWidgetDeviation.setColumnCount(0)
552
        self.tableWidgetDeviation.setRowCount(0)
553

    
554
        app_doc_data = AppDocData.instance()
555
        app_doc_data.outputs.clear()
556

    
557
    def clear_loop(self):
558
        self.tableWidgetLoop.setColumnCount(0)
559

    
560
    def initTableWidgetHMB(self):
561
        app_doc_data = AppDocData.instance()
562

    
563
        column_infos = app_doc_data.getHMBDisplayNameAndUnitsExpression()
564
        """ convert unit """
565
        for row in range(column_infos.rowCount()):
566
            item = column_infos.item(row, 1)
567
            unit = self.convertToUnits(item.text())
568
            item.setText(unit)
569

    
570
        self.tableWidgetHMB.setModel(column_infos)
571

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

    
606
        # self.tableWidgetHMB.setEditTriggers(QAbstractItemView.NoEditTriggers)
607
        self.tableWidgetHMB.verticalHeader().setVisible(False)
608

    
609
        col_names = ['Stream No.\n', 'Unit\n']
610
        column_infos.setHorizontalHeaderLabels(col_names)
611

    
612
        self.tableWidgetHMB.resizeColumnsToContents()
613
        self.tableWidgetHMB.resizeRowsToContents()
614

    
615
    def copy_selection(self, table_widget):
616
        """copy selected text to clipboard"""
617

    
618
        import io
619
        import csv
620

    
621
        selection = table_widget.selectedIndexes()
622
        if selection:
623
            rows = sorted(index.row() for index in selection)
624
            columns = sorted(index.column() for index in selection)
625
            rowcount = rows[-1] - rows[0] + 1
626
            colcount = columns[-1] - columns[0] + 1
627
            table = [[''] * colcount for _ in range(rowcount)]
628
            for index in selection:
629
                row = index.row() - rows[0]
630
                column = index.column() - columns[0]
631
                table[row][column] = index.data()
632
            stream = io.StringIO()
633
            csv.writer(stream, delimiter='\t').writerows(table)
634
            QApplication.clipboard().setText(stream.getvalue())
635

    
636
    def paste_selection(self, table_widget):
637
        """paste text of clipboard to table widget"""
638

    
639
        import io
640
        import csv
641

    
642
        selection = table_widget.selectedIndexes()
643
        if selection:
644
            model = table_widget.model()
645

    
646
            buffer = QApplication.clipboard().text()
647
            rows = sorted(index.row() for index in selection)
648
            columns = sorted(index.column() for index in selection)
649
            reader = csv.reader(io.StringIO(buffer), delimiter='\t')
650
            if len(rows) == 1 and len(columns) == 1:
651
                for i, line in enumerate(reader):
652
                    for j, cell in enumerate(line):
653
                        index = model.index(rows[0] + i, columns[0] + j)
654
                        item = model.item(index.row(), index.column())
655
                        if item.isEditable():
656
                            data = model.data(index, Qt.UserRole)
657
                            if type(data) is not type(cell) or data != cell:
658
                                item.setBackground(Qt.magenta)
659
                                model.setData(index, cell)
660
            else:
661
                arr = [[cell for cell in row] for row in reader]
662
                for index in selection:
663
                    row = index.row() - rows[0]
664
                    column = index.column() - columns[0]
665
                    index = model.index(index.row(), index.column())
666
                    item = model.item(index.row(), index.column())
667
                    if item.isEditable():
668
                        data = model.data(index, Qt.UserRole)
669
                        if type(data) is not type(arr[row][column]) or data != arr[row][column]:
670
                            item.setBackground(Qt.magenta)
671
                            model.setData(index, arr[row][column])
672

    
673
    '''
674
        @brief      Drawing 속성의 Units 에서 Key값을 이용하여 Value를 찾음
675
        @author     yeonjin
676
        @date       19.08.30
677
    '''
678

    
679
    def findValue(self, key):
680
        value = None
681

    
682
        key = key.replace("{", "").replace("}", "")
683
        for attr in AppDocData.instance().activeDrawing.attrs:
684
            if attr[0] == 'Units':
685
                if key in attr[1]:
686
                    value = attr[1][key]
687
                    break
688

    
689
        return value
690

    
691
    '''
692
        @brief      Convert UnitsExpression To Units Value
693
        @author     yeonjin
694
        @date       19.08.29
695
    '''
696

    
697
    def convertToUnits(self, unitsExpression):
698
        import re
699

    
700
        if unitsExpression is None or AppDocData.instance().activeDrawing is None:
701
            return ''
702

    
703
        found = re.findall('{.*?}', unitsExpression)
704
        for f in found:
705
            key = f
706
            val = self.findValue(key)
707
            if val:
708
                unitsExpression = unitsExpression.replace(key, val)
709

    
710
        return unitsExpression
711

    
712
    def on_left_alignment(self):
713
        """align selected items to left"""
714

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

    
728
    def on_right_alignment(self):
729
        """align selected items to right"""
730

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

    
744
    def on_up_alignment(self):
745
        """align selected items to up"""
746

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

    
760
    def on_down_alignment(self):
761
        """align selected items to down"""
762

    
763
        try:
764
            app_doc_data = AppDocData.instance()
765
            if app_doc_data._activeDrawing is not None:
766
                items = self.graphicsView.scene.selectedItems()
767
                if items:
768
                    self.graphicsView.scene._undo_stack.push(
769
                        DownAlignmentCommand.DownAlignmentCommand(self.graphicsView.scene,
770
                                                                  self.graphicsView.scene.selectedItems()))
771
        except Exception as ex:
772
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
773
                      f"{sys.exc_info()[-1].tb_lineno}"
774
            self.addMessage.emit(MessageType.Error, message)
775

    
776
    def load_drawing_list(self):
777
        """load pfd drawing list"""
778

    
779
        try:
780
            app_doc_data = AppDocData.instance()
781
            drawings = app_doc_data.getDrawings()
782

    
783
            if self.treeViewDrawingList.model():
784
                self.treeViewDrawingList.model().clear()
785

    
786
            model = QStandardItemModel()
787
            root = [QStandardItem(self.tr('Drawings')), QStandardItem('')]
788
            model.appendRow(root)
789

    
790
            for drawing in drawings:
791
                items = [QStandardItem(drawing.name), QStandardItem(drawing.date_time), QStandardItem(drawing.path)]
792
                items[0].setIcon(QIcon(':images/PFD.png'))
793
                items[0].setData(drawing, Qt.UserRole)
794
                root[0].appendRow(items)
795

    
796
            model.setHorizontalHeaderLabels([self.tr('Name'), self.tr('DateTime'), self.tr('Path')])
797
            self.treeViewDrawingList.setModel(model)
798

    
799
            root[0].setText(self.tr('Drawings') + f"({root[0].rowCount()})")
800
            self.treeViewDrawingList.expandAll()
801
            self.treeViewDrawingList.sortByColumn(1, Qt.DescendingOrder)
802
            self.treeViewDrawingList.resizeColumnToContents(0)
803
            self.treeViewDrawingList.hideColumn(2)
804
        except Exception as ex:
805
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
806
                      f"{sys.exc_info()[-1].tb_lineno}"
807
            self.addMessage.emit(MessageType.Error, message)
808

    
809
    def save_drawing_if_necessary(self):
810
        """ask user to save drawing or not when drawing is modified"""
811

    
812
        app_doc_data = AppDocData.instance()
813
        if app_doc_data.activeDrawing and app_doc_data.activeDrawing.modified:
814
            if QMessageBox.Yes == QMessageBox.question(self, self.tr("Question"),
815
                                                       self.tr("Do you want to save drawing?"),
816
                                                       QMessageBox.Yes | QMessageBox.No):
817
                self.actionSaveCliked(False)
818

    
819
    def open_selected_drawing(self, index: QModelIndex):
820
        """ open selected drawing """
821
        try:
822
            model = self.treeViewDrawingList.model()
823
            root = index.parent()
824
            if root:
825
                file_path = model.data(model.index(index.row(), 2, root), Qt.DisplayRole)
826
                if os.path.exists(file_path):
827
                    self.save_drawing_if_necessary()
828
                    self.open_drawing(file_path)
829
        except Exception as ex:
830
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
831
                      f"{sys.exc_info()[-1].tb_lineno}"
832
            self.addMessage.emit(MessageType.Error, message)
833

    
834
    def actionSaveCliked(self, show_message=True):
835
        """action save click event"""
836
        from datetime import datetime
837
        from AppDocData import AppDocData
838
        from EngineeringAbstractItem import QEngineeringAbstractItem
839

    
840
        try:
841
            app_doc_data = AppDocData.instance()
842
            if app_doc_data.activeDrawing is None:
843
                self.showImageSelectionMessageBox()
844
                return
845

    
846
            app_doc_data.activeDrawing.clearItemList()
847

    
848
            items = self.graphicsView.scene.items()
849
            for item in items:
850
                if issubclass(type(item), QEngineeringAbstractItem):
851
                    app_doc_data.activeDrawing.allItems.append(item)
852

    
853
            count = len(app_doc_data.activeDrawing.allItems)
854
            if count > 0:
855
                try:
856
                    self.progress = QProgressDialog(self.tr("Please wait for a while"), self.tr("Cancel"), 0, 100,
857
                                                    self) if not hasattr(self, 'progress') else self.progress
858
                    self.progress.setWindowModality(Qt.WindowModal)
859
                    self.progress.setAutoReset(True)
860
                    self.progress.setAutoClose(True)
861
                    self.progress.setMinimum(0)
862
                    self.progress.resize(600, 100)
863
                    self.progress.setWindowTitle(self.tr("Save"))
864
                    self.progress.show()
865

    
866
                    self.save_drawing_data()
867
                finally:
868
                    self.load_drawing_list()
869
                    self.progress.setValue(self.progress.maximum())
870
                    self.progress.hide()
871
                    if show_message:
872
                        QMessageBox.information(self, self.tr("Information"), self.tr("Save completed successfully."))
873

    
874
        except Exception as ex:
875
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
876
                      f"{sys.exc_info()[-1].tb_lineno}"
877
            self.addMessage.emit(MessageType.Error, message)
878

    
879
    def on_save(self):
880
        self.actionSaveCliked()
881

    
882
    def on_save_as(self):
883
        """save as drawing file"""
884
        from shutil import copyfile
885
        from datetime import datetime
886
        from Drawing import Drawing
887

    
888
        app_doc_data = AppDocData.instance()
889
        workspace = self.get_work_space()
890

    
891
        file_name = os.path.join(workspace, 'copy of ' + app_doc_data.activeDrawing.name + '.hytos')
892
        options = QFileDialog.Options()
893
        options |= QFileDialog.DontUseNativeDialog
894
        name, _ = QFileDialog.getSaveFileName(self, self.tr('Save As'), file_name, 'HYTOS(*.hytos)', options=options)
895
        if name:
896
            try:
897
                if os.path.splitext(name)[1] != '.hytos': name += '.hytos'
898

    
899
                self.actionSaveCliked()  # save current drawing
900

    
901
                app_doc_data = AppDocData.instance()
902
                # copy current drawing file to new drawing file
903
                copyfile(app_doc_data.activeDrawing.path, name)
904
                app_doc_data.activeDrawing.path = name
905

    
906
                matches = [drawing for drawing in app_doc_data.getDrawings()
907
                           if os.path.exists(drawing.path) and os.path.samefile(drawing.path, name)]
908
                if not matches:
909
                    drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
910
                    app_doc_data.saveDrawing(drawing)
911
                else:
912
                    drawing = Drawing(str(matches[0].UID), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
913
                    app_doc_data.updateDrawing(drawing)
914

    
915
                self.load_drawing_list()
916
                self.open_border_file()
917
                self.load_data(drawing)
918

    
919
                app_doc_data.activeDrawing.modified = False
920
                self.setMainWindowTitle(f"{app_doc_data.activeDrawing.path}")
921
            except Exception as ex:
922
                message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
923
                                                               sys.exc_info()[-1].tb_lineno)
924
                self.addMessage.emit(MessageType.Error, message)
925

    
926
    def on_undo(self):
927
        """undo"""
928
        self.graphicsView.scene.undo_stack.undo()
929

    
930
    def on_redo(self):
931
        """redo"""
932
        self.graphicsView.scene.undo_stack.redo()
933

    
934
    def save_drawing_data(self):
935
        """ save drawing data """
936

    
937
        from datetime import datetime
938
        from AppDocData import AppDocData
939
        from SymbolSvgItem import SymbolSvgItem
940
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
941
        from EngineeringCalloutTextItem import QEngineeringCalloutTextItem
942
        from EngineeringCloudItem import QEngineeringCloudItem
943
        from EngineeringDimensionItem import QEngineeringDimensionItem
944

    
945
        try:
946
            app_doc_data = AppDocData.instance()
947
            items = app_doc_data.activeDrawing.allItems
948

    
949
            maxValue = len(items)
950
            self.progress.setMaximum(maxValue)
951

    
952
            app_doc_data.saveToDatabase([item for item in self.graphicsView.scene.items()
953
                                         if hasattr(item, 'toSql') and (
954
                                                 type(item) is SymbolSvgItem or
955
                                                 type(item) is QEngineeringStreamlineItem or
956
                                                 type(item) is QEngineeringCalloutTextItem or
957
                                                 type(item) is QEngineeringDimensionItem or
958
                                                 type(item) is QEngineeringCloudItem)],
959
                                        self.progress.setValue)
960

    
961
            if app_doc_data.activeDrawing:
962
                app_doc_data.activeDrawing.hmbTable.saveData()
963
                app_doc_data.save_sheet_history('Save')
964

    
965
            """update drawing's modified time"""
966
            drawings = app_doc_data.getDrawings()
967
            drawing = [drawing for drawing in drawings if app_doc_data.activeDrawing.UID == drawing.UID]
968
            if drawing:
969
                drawing[0].date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
970
                rect = self.graphicsView.viewport().rect()
971
                drawing[0].view_rect = self.graphicsView.mapToScene(rect).boundingRect()
972
                app_doc_data.updateDrawing(drawing[0])
973

    
974
            app_doc_data.activeDrawing.modified = False
975
            self.setMainWindowTitle(f"{app_doc_data.activeDrawing.path}")
976

    
977
        except Exception as ex:
978
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
979
                      f"{sys.exc_info()[-1].tb_lineno}"
980
            self.addMessage.emit(MessageType.Error, message)
981

    
982
    '''
983
        @brief  add message listwidget
984
        @author humkyung
985
        @date   2018.07.31
986
    '''
987

    
988
    def onAddMessage(self, messageType, message):
989
        from AppDocData import MessageType
990

    
991
        try:
992
            current = QDateTime.currentDateTime()
993

    
994
            item = QListWidgetItem('{}: {}'.format(current.toString('hh:mm:ss'), message))
995
            if messageType == MessageType.Error:
996
                item.setForeground(Qt.red)
997
            elif messageType == MessageType.Information:
998
                item.setForeground(Qt.blue)
999

    
1000
            self.listWidgetLogs.insertItem(0, item)
1001
        except Exception as ex:
1002
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1003
                                                       sys.exc_info()[-1].tb_lineno))
1004
        finally:
1005
            logs = self.listWidgetLogs.count()
1006
            if logs:
1007
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs({})'.format(logs))
1008
            else:
1009
                self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogs), 'Logs')
1010

    
1011
    '''
1012
        @brief      Area Zoom
1013
        @author     Jeongwoo
1014
        @date       2018.06.27
1015
        @history    connect command's rejected signal
1016
    '''
1017

    
1018
    def onAreaZoom(self, action):
1019
        if self.actionZoom.isChecked():
1020
            cmd = AreaZoomCommand.AreaZoomCommand(self.graphicsView)
1021
            cmd.onRejected.connect(self.onCommandRejected)
1022
            self.graphicsView.command = cmd
1023

    
1024
    def fitWindow(self, action):
1025
        """fit window"""
1026
        app_doc_data = AppDocData.instance()
1027
        if app_doc_data.activeDrawing:
1028
            self.graphicsView.useDefaultCommand()
1029
            scene_rect = self.graphicsView.scene.itemsBoundingRect()
1030
            if scene_rect.left() > 0:
1031
                scene_rect.setLeft(0)
1032
            if scene_rect.top() > 0:
1033
                scene_rect.setTop(0)
1034
            if scene_rect.right() < 1189:
1035
                scene_rect.setRight(1189)
1036
            if scene_rect.bottom() < 841:
1037
                scene_rect.setBottom(841)
1038

    
1039
            self.graphicsView.setSceneRect(scene_rect)
1040
            if action is None and app_doc_data.activeDrawing.view_rect.isValid():
1041
                self.graphicsView.zoom_rect(app_doc_data.activeDrawing.view_rect)
1042
            else:
1043
                self.graphicsView.zoomImageInit()
1044
            self.graphicsView.scene.invalidate()
1045

    
1046
    def on_view_connector(self, checked):
1047
        """turn on/off connector"""
1048
        for item in self.graphicsView.scene.items():
1049
            if not hasattr(item, 'connectors'):
1050
                continue
1051
            for connector in item.connectors:
1052
                connector.setVisible(True) if checked else connector.hide()
1053

    
1054
    def scene_changed(self):
1055
        """update modified flag"""
1056

    
1057
        app_doc_data = AppDocData.instance()
1058
        app_doc_data.activeDrawing.modified = True
1059
        self.setMainWindowTitle(f"{app_doc_data.activeDrawing.path}*")
1060

    
1061
    '''
1062
        @brief      selection changed
1063
        @author     humkyung
1064
        @date       2018.06.27
1065
        @history    humkung 2018.07.08 call tree widget's findItem
1066
    '''
1067

    
1068
    def onSelectionChanged(self):
1069
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
1070

    
1071
        items = [item for item in self.graphicsView.scene.selectedItems() if
1072
                 issubclass(type(item), SymbolSvgItem) or type(item) is QEngineeringStreamlineItem]
1073
        if items:
1074
            item = items[-1]
1075

    
1076
            if type(item) is QEngineeringErrorItem:
1077
                for index in range(self.tableWidgetHMB.rowCount()):
1078

    
1079
                    if self.tableWidgetHMB.item(index, 1).tag is item:
1080
                        self.tableWidgetHMB.selectRow(index)
1081
                        break
1082

    
1083
    '''
1084
        @brief      Initialize scene and itemTreeWidget
1085
        @author     Jeongwoo
1086

1087
        @date       2018.06.14
1088
        @history    humkyung 2018.08.16 ask to delete recognized items before remove
1089
    '''
1090

    
1091
    def onInitializeScene(self, action):
1092
        try:
1093
            msg = QMessageBox(self)
1094
            msg.setIcon(QMessageBox.Question)
1095
            msg.setText(self.tr('Do you want to remove all items?\nThis work cannot be recovered.'))
1096
            msg.setWindowTitle(self.tr("Clear Screen"))
1097
            msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
1098
            if QMessageBox.Ok == msg.exec_():
1099

    
1100
                appDocData = AppDocData.instance()
1101
                appDocData.activeDrawing.hmbTable.reset()
1102
                appDocData.clearItemList(True)
1103

    
1104
                items = self.graphicsView.scene.items()
1105
                for item in items:
1106
                    if type(item) is not QGraphicsPixmapItem and item.scene() is not None:
1107
                        self.graphicsView.scene.removeItem(item)
1108

    
1109
                appDocData.initializeDataByDrawingUID(appDocData.activeDrawing.UID)
1110

    
1111
                self.initTableWidgetHMB()
1112

    
1113
        except Exception as ex:
1114
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1115
                                                           sys.exc_info()[-1].tb_lineno)
1116
            self.addMessage.emit(MessageType.Error, message)
1117

    
1118
    '''
1119
        @brief      Manage Checkable Action statement
1120
        @author     Jeongwoo
1121
        @date       2018.05.10
1122
        @history    2018.06.27  Jeongwoo    Chnage method to initialize command [Set None → DefaultCommand]
1123
    '''
1124

    
1125
    def actionGroupTriggered(self, action):
1126
        if hasattr(self.actionLine, 'tag'):
1127
            self.actionLine.tag.onRejected.emit(None)
1128

    
1129
        if self.graphicsView.command is not None:
1130
            self.graphicsView.useDefaultCommand()
1131

    
1132
        for _action in [x for x in self.actionGroup.actions() if x is not action]:
1133
            _action.setChecked(False)
1134
            if _action is self.actionLine:
1135
                for item in self.graphicsView.scene.items():
1136
                    if not hasattr(item, 'connectors'): continue
1137
                    for connector in item.connectors: connector.hide()
1138

    
1139
        action.setChecked(True)
1140
        if action is self.actionLine:
1141
            for item in self.graphicsView.scene.items():
1142
                if not hasattr(item, 'connectors'): continue
1143
                for connector in item.connectors: connector.setVisible(True)
1144

    
1145
    def showSymbolEditor(self):
1146
        from SymbolEditorDialog import QSymbolEditorDialog
1147

    
1148
        dlg = QSymbolEditorDialog()
1149
        dlg.exec_()
1150

    
1151
    def editor_lost_focus(self, item):
1152
        cursor = item.textCursor()
1153
        cursor.clearSelection()
1154
        item.setTextCursor(cursor)
1155

    
1156
        """
1157
        if item.toPlainText():
1158
            self.removeItem(item)
1159
            item.deleteLater()
1160
        """
1161

    
1162
    def item_selected(self, item):
1163
        pass
1164
        """
1165
        font = item.font()
1166
        color = item.defaultTextColor()
1167
        self.fontCombo.setCurrentFont(font)
1168
        self.fontSizeCombo.setEditText(str(font.pointSize()))
1169
        self.boldAction.setChecked(font.weight() == QFont.Bold)
1170
        self.italicAction.setChecked(font.italic())
1171
        self.underlineAction.setChecked(font.underline())
1172
        """
1173

    
1174
    def on_callout_created(self):
1175
        from CreateCommand import CreateCommand
1176
        try:
1177
            QApplication.restoreOverrideCursor()
1178

    
1179
            callout = self.actionPlaceTextBox.tag.callout
1180
            callout.setTextInteractionFlags(Qt.TextEditorInteraction)
1181
            callout.setFocus()
1182
            callout.lost_focus.connect(self.editor_lost_focus)
1183
            callout.selected_change.connect(self.item_selected)
1184

    
1185
            self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [callout, ]))
1186
        finally:
1187
            self.actionPlaceTextBox.tag.reset()
1188

    
1189
    def on_place_callout_textbox(self):
1190
        """place callout textbox"""
1191
        from PlaceCalloutCommand import PlaceCalloutCommand
1192

    
1193
        if not hasattr(self.actionPlaceTextBox, 'tag'):
1194
            self.actionPlaceTextBox.tag = PlaceCalloutCommand(self.graphicsView)
1195

    
1196
        self.actionPlaceTextBox.tag.onSuccess.connect(self.on_callout_created)
1197
        self.actionPlaceTextBox.tag.onRejected.connect(self.onCommandRejected)
1198

    
1199
        self.graphicsView.command = self.actionPlaceTextBox.tag
1200

    
1201
    def on_dimension_created(self):
1202
        from CreateCommand import CreateCommand
1203
        try:
1204
            QApplication.restoreOverrideCursor()
1205

    
1206
            dimension = self.actionPlaceDimension.tag.dimension
1207
            dimension.transfer.onRemoved.connect(self.on_item_removed)
1208

    
1209
            self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [dimension, ]))
1210
        finally:
1211
            self.actionPlaceDimension.tag.reset()
1212

    
1213
    def on_place_dimension(self):
1214
        """place dimension"""
1215
        from PlaceDimensionCommand import PlaceDimensionCommand
1216

    
1217
        if not hasattr(self.actionPlaceDimension, 'tag'):
1218
            self.actionPlaceDimension.tag = PlaceDimensionCommand(self.graphicsView)
1219

    
1220
        self.actionPlaceDimension.tag.onSuccess.connect(self.on_dimension_created)
1221
        self.actionPlaceDimension.tag.onRejected.connect(self.onCommandRejected)
1222

    
1223
        self.graphicsView.command = self.actionPlaceDimension.tag
1224

    
1225
    def on_place_cloud(self):
1226
        """place a cloud"""
1227
        from PlaceCloudCommand import PlaceCloudCommand
1228

    
1229
        def on_cloud_created():
1230
            from CreateCommand import CreateCommand
1231
            try:
1232
                QApplication.restoreOverrideCursor()
1233

    
1234
                cloud = self.actionPlaceCloud.tag.cloud
1235
                self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [cloud, ]))
1236
            finally:
1237
                self.actionPlaceCloud.tag.reset()
1238

    
1239
        if not hasattr(self.actionPlaceCloud, 'tag'):
1240
            self.actionPlaceCloud.tag = PlaceCloudCommand(self.graphicsView)
1241

    
1242
        self.actionPlaceCloud.tag.onSuccess.connect(on_cloud_created)
1243
        self.actionPlaceCloud.tag.onRejected.connect(self.onCommandRejected)
1244

    
1245
        self.graphicsView.command = self.actionPlaceCloud.tag
1246

    
1247
    def on_help(self):
1248
        """open user manual"""
1249
        import os
1250

    
1251
        try:
1252
            help_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'HYTOS User Manual.pdf')
1253
            os.startfile(f"\"{help_file_path}\"")
1254
            # os.system(f"\"{help_file_path}\"")
1255
        except Exception as ex:
1256
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1257
                                                           sys.exc_info()[-1].tb_lineno)
1258
            self.addMessage.emit(MessageType.Error, message)
1259

    
1260
    def on_about(self):
1261
        """show about dialog"""
1262
        from AboutDialog import QAboutDialog
1263

    
1264
        dlg = QAboutDialog()
1265
        dlg.exec_()
1266

    
1267
    def on_readme(self):
1268
        """open readme.html"""
1269

    
1270
        file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'readme.html')
1271
        os.startfile(f"\"{file_path}\"")
1272

    
1273
    def update_label_contents(self):
1274
        from EngineeringCalloutTextItem import QEngineeringCalloutTextItem
1275

    
1276
        items = [item for item in self.graphicsView.scene.items() if issubclass(type(item), SymbolSvgItem) or
1277
                 type(item) is QEngineeringStreamlineItem or type(item) is QEngineeringCalloutTextItem]
1278
        for item in items:
1279
            item.update_label_contents()
1280

    
1281
    def onOptions(self):
1282
        from OptionsDialog import QOptionsDialog
1283
        try:
1284
            app_doc_data = AppDocData.instance()
1285
            configs = app_doc_data.getAppConfigs('option', 'TagFontSize')
1286
            old_tag_font_size = configs[0].value if configs and len(configs) == 1 else 6
1287

    
1288
            configs = app_doc_data.getAppConfigs('option', 'TagFontColor')
1289
            old_tag_font_color = configs[0].value if configs and len(configs) == 1 else '#000000'
1290

    
1291
            configs = app_doc_data.getAppConfigs('option', 'CalloutFontSize')
1292
            old_callout_font_size = configs[0].value if configs and len(configs) == 1 else 6
1293

    
1294
            configs = app_doc_data.getAppConfigs('option', 'CalloutTextColor')
1295
            old_callout_text_color = configs[0].value if configs and len(configs) == 1 else '#000000'
1296

    
1297
            dlg = QOptionsDialog(self)
1298
            dlg.setWindowFlags(dlg.windowFlags() & ~Qt.WindowContextHelpButtonHint)
1299
            if QDialog.Accepted == dlg.exec_():
1300
                if app_doc_data.activeDrawing:
1301
                    if str(old_tag_font_size) != str(dlg.tag_font_size) or \
1302
                            old_tag_font_color != dlg.tag_text_color or \
1303
                            str(old_callout_font_size) != str(dlg.callout_font_size) or \
1304
                            old_callout_text_color != dlg.callout_text_color:
1305
                        self.update_label_contents()
1306

    
1307
        except Exception as ex:
1308
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1309
                                                           sys.exc_info()[-1].tb_lineno)
1310
            self.addMessage.emit(MessageType.Error, message)
1311

    
1312
    def change_output_font_color(self):
1313
        try:
1314
            row_count = self.tableWidgetDeviation.rowCount()
1315
            col_count = self.tableWidgetDeviation.columnCount()
1316
            for row in range(row_count):
1317
                for col in range(col_count):
1318
                    if row == 0:
1319
                        item = self.tableWidgetDeviation.item(row, col)
1320
                        item.setText('RUN CALCULATION')
1321
                        item.setForeground(Qt.red)
1322
                        self.tableWidgetDeviation.setItem(row, col, item)
1323
                    else:
1324
                        self.tableWidgetDeviation.item(row, col).setForeground(QBrush(QColor(169, 169, 169)))
1325

    
1326
            row_count = self.tableWidgetOutput.rowCount()
1327
            col_count = self.tableWidgetOutput.columnCount()
1328
            for row in range(row_count):
1329
                for col in range(col_count):
1330
                    self.tableWidgetOutput.item(row, col).setForeground(QBrush(QColor(169, 169, 169)))
1331
                    col_span = self.tableWidgetOutput.columnSpan(row, col)
1332
                    if col_span > 1:
1333
                        break
1334
        except Exception as ex:
1335
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1336
                                                           sys.exc_info()[-1].tb_lineno)
1337
            self.addMessage.emit(MessageType.Error, message)
1338

    
1339
    def calculation(self, saving=False):
1340
        """execute hydro calculation"""
1341
        from AppDocData import AppDocData
1342
        from Calculation import Calculation
1343
        from HydroCalculationCommand import HydroCalculationCommand
1344
        from CalculationValidation import QCalculationValidation
1345
        from ValidationDialog import QValidationDialog
1346

    
1347
        try:
1348
            app_doc_data = AppDocData.instance()
1349
            if app_doc_data.activeDrawing is None:
1350
                self.showImageSelectionMessageBox()
1351
                return
1352

    
1353
            hmbs = app_doc_data.activeDrawing.hmbTable._hmbs
1354
            if hmbs is not None:
1355
                try:
1356
                    errors = []
1357
                    items = [item for item in self.graphicsView.scene.items() if type(item) is SymbolSvgItem or
1358
                             type(item) is QEngineeringStreamlineItem]
1359
                    for item in items:
1360
                        error = item.validate()
1361
                        if error:
1362
                            errors.extend(error)
1363

    
1364
                    HydroCalculationCommand.ERRORS = 0
1365
                    if errors:
1366
                        dlg = QValidationDialog(self, errors)
1367
                        dlg.show()
1368
                        HydroCalculationCommand.ERRORS = len(errors)
1369
                    else:
1370
                        self.progress = QProgressDialog(self.tr("Please wait for a while"), self.tr("Cancel"), 0, 100, self) \
1371
                            if not hasattr(self, 'progress') else self.progress
1372
                        self.progress.setWindowModality(Qt.WindowModal)
1373
                        self.progress.setAutoReset(True)
1374
                        self.progress.setAutoClose(True)
1375
                        self.progress.setMinimum(0)
1376
                        self.progress.resize(600, 100)
1377
                        self.progress.setWindowTitle(self.tr("Calculate data..."))
1378
                        self.progress.show()
1379

    
1380
                        maxValue = len(hmbs)
1381
                        self.progress.setMaximum(maxValue)
1382

    
1383
                        for hmb in hmbs:
1384
                            self.progress.setValue(self.progress.value() + 1)
1385

    
1386
                            if hmb.phase_type:
1387
                                Calculation(hmb)
1388

    
1389
                            QApplication.processEvents()
1390

    
1391
                        messages = None
1392
                        """ generate loop """
1393
                        cmd = HydroCalculationCommand(self.graphicsView)
1394
                        messages = cmd.execute(None)
1395
                        cmd.execute_second(None)
1396

    
1397
                        app_doc_data.activeDrawing.loops = cmd.loops
1398

    
1399
                        self.display_loops()
1400
                        self.display_output()
1401

    
1402
                        self.load_HMB()
1403

    
1404
                        """pop up error message dialog if there are some errors"""
1405
                        if messages:
1406
                            dlg = QCalculationValidation()
1407
                            dlg.show_dialog('Calculation will be terminated!', messages)
1408

    
1409
                        if saving:
1410
                            app_doc_data.save_sheet_history('Calculation')
1411
                finally:
1412
                    self.progress.setValue(self.progress.maximum())
1413
                    self.progress.hide()
1414
        except Exception as ex:
1415
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1416
                      f"{sys.exc_info()[-1].tb_lineno}"
1417
            self.addMessage.emit(MessageType.Error, message)
1418

    
1419
    def on_generate_report(self):
1420
        """generate calculation report"""
1421
        from tempfile import NamedTemporaryFile
1422
        import openpyxl
1423
        from DrawImage import DrawImage
1424
        from openpyxl.styles import Font
1425
        from datetime import datetime
1426
        # from EngineeringStreamlineItem import QEngineeringStreamlineItem
1427

    
1428
        try:
1429
            app_doc_data = AppDocData.instance()
1430
            if app_doc_data.activeDrawing is None:
1431
                self.showImageSelectionMessageBox()
1432
                return
1433

    
1434
            image_path = None
1435
            with NamedTemporaryFile() as f:
1436
                image_path = f.name + '.png'
1437

    
1438
            if image_path:
1439
                self.on_view_connector(False)  # hide connector
1440
                self.graphicsView.save_as_image(image_path)
1441

    
1442
                workspace = self.get_work_space()
1443
                file_name = os.path.join(workspace, app_doc_data.activeDrawing.name + '.xlsx')
1444
                options = QFileDialog.Options()
1445
                options |= QFileDialog.DontUseNativeDialog
1446
                file_name, _ = QFileDialog.getSaveFileName(self, self.tr('Report'), file_name, 'Excel(*.xlsx)',
1447
                                                           options=options)
1448

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

    
1451
                # get image file path
1452
                company_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Company.png')
1453
                horizontal_flow_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Horizontal_Flow.png')
1454
                vertical_upward_flow_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME,
1455
                                                               'Vertical_Upward_Flow.png')
1456
                vertical_downward_flow_image_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME,
1457
                                                                 'Vertical_Downward_Flow.png')
1458
                # up to here
1459
                if file_name and os.path.exists(template):
1460
                    units = [attr[1] for attr in app_doc_data.activeDrawing.attrs if attr[0] == 'Units'][0]
1461

    
1462
                    wb = openpyxl.load_workbook(template)
1463

    
1464
                    page_no = 1
1465
                    pages = math.ceil(len(list(app_doc_data.activeDrawing.hmbTable.streamNos())) / 20)
1466
                    for page in range(pages):
1467
                        ws = wb.copy_worksheet(wb.get_sheet_by_name('Page'))
1468
                        ws.title = f"Page ({page_no})"
1469

    
1470
                        cal_image = openpyxl.drawing.image.Image(image_path)
1471
                        # rect = self.graphicsView.sceneRect()
1472
                        x_scale = MainWindow.DEFAULT_SIZE.width() / cal_image.width
1473
                        y_scale = MainWindow.DEFAULT_SIZE.height() / cal_image.height
1474
                        scale = min(x_scale, y_scale)
1475
                        cal_image.width *= scale
1476
                        cal_image.height *= scale
1477
                        ws.add_image(cal_image, 'C4')
1478

    
1479
                        company_image = openpyxl.drawing.image.Image(company_image_path)
1480
                        company_image.width *= 0.8
1481
                        company_image.height *= 0.7
1482
                        ws.add_image(company_image, 'X54')
1483

    
1484
                        """write sheet information"""
1485
                        configs = app_doc_data.getConfigs('Sheet')
1486
                        for config in configs:
1487
                            if config.key == 'JobNo' and config.value:
1488
                                ws['Z59'].value = config.value
1489
                            elif config.key == 'JobName' and config.value:
1490
                                ws['W60'].value = config.value
1491
                            elif config.key == 'Description' and config.value:
1492
                                ws['W74'].value = config.value
1493
                            elif config.key == 'RevStatus' and config.value:
1494
                                ws['Y69'].value = config.value
1495
                            elif config.key == 'CheckedBy' and config.value:
1496
                                ws['Z79'].value = config.value
1497
                            elif config.key == 'ApprovedBy' and config.value:
1498
                                ws['Z80'].value = config.value
1499

    
1500
                        configs = app_doc_data.getAppConfigs('app', 'SingleID')
1501
                        if configs and configs[0].value:
1502
                            ws['Z78'].value = configs[0].value
1503
                        ws['Z69'].value = str(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
1504
                        """up to here"""
1505

    
1506
                        # Output [Pump, Compressor, Control Valve]
1507
                        ws['X1'].value = 'Loop_Deviation'
1508
                        ws['X1'].font = Font(bold=True, underline='single', size=11)
1509

    
1510
                        loops = app_doc_data.activeDrawing.loops
1511
                        strmsg = None
1512
                        row_no = 2
1513
                        for loop in loops:
1514
                            deviation = loop.deviation
1515
                            if round(deviation, 9) == 0:
1516
                                continue
1517

    
1518
                            for i in range(1, len(loop.items) - 1, 3):
1519
                                if i == 1:
1520
                                    loop_no = loop.name.replace('Loop', '')
1521
                                    if len(loop_no) > 1:
1522
                                        strmsg = '({}) {}'.format(loop_no, loop.items[i].stream_no)
1523
                                    else:
1524
                                        strmsg = '(0{}) {}'.format(loop_no, loop.items[i].stream_no)
1525
                                else:
1526
                                    if len(str(loop.items[i - 1])) > 13 and str(loop.items[i - 1])[
1527
                                                                            :13] == 'Line_Splitter':
1528
                                        pre_stream_num = loop.items[i - 3].stream_no
1529
                                        cur_stream_num = loop.items[i].stream_no
1530

    
1531
                                        if strmsg.__contains__('-'):
1532
                                            last_stream_num = str(strmsg).rsplit('-', 1)[1]
1533
                                        else:
1534
                                            last_stream_num = str(strmsg).rsplit(' ', 1)[1]
1535

    
1536
                                        if pre_stream_num != int(last_stream_num):
1537
                                            strmsg = '{}-{}-{}'.format(strmsg, pre_stream_num, cur_stream_num)
1538

    
1539
                            last_stream_num = loop.items[len(loop.items) - 2].stream_no
1540
                            strmsg = '{}-{}'.format(strmsg, last_stream_num)
1541

    
1542
                            value = '{} = {} {}'.format(strmsg, round(deviation, 3), units['Pressure'])
1543
                            if round(deviation, 3) < 0:
1544
                                ws.cell(row_no, 24, value).font = Font(underline='single',
1545
                                                                       size=10,
1546
                                                                       color='FF0000')
1547

    
1548
                            elif round(deviation, 3) > 0:
1549
                                ws.cell(row_no, 24, value).font = Font(size=10)
1550
                            else:
1551
                                ws.cell(row_no, 24, value).font = Font(underline='single',
1552
                                                                       size=10)
1553
                            row_no += 1
1554

    
1555
                        start_row_no = row_no + 1
1556
                        start_col_no = 24
1557
                        # Pump
1558
                        pumps = []
1559
                        row_no = start_row_no
1560
                        col_no = start_col_no
1561
                        for loop in loops:
1562
                            for item in loop.items:
1563
                                parent = item.parent
1564
                                if parent:
1565
                                    name = str(item).replace(
1566
                                        '_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
1567
                                    if name in pumps:
1568
                                        continue
1569
                                    if name[:3] == 'R_P' or name[:3] == 'L_P' or name[:3] == 'V_P':
1570
                                        pumps.append(name)
1571

    
1572
                                        attr = item.parent.attribute
1573
                                        ws.cell(row_no, col_no, attr['Tag_No']).font = Font(bold=True,
1574
                                                                                            underline='single',
1575
                                                                                            size=11)
1576

    
1577
                                        row_no += 1
1578
                                        ws.cell(row_no, col_no, 'Suct.P :')
1579
                                        col_no += 2
1580
                                        ws.cell(row_no, col_no, round(attr['Suct.P'], 3))
1581
                                        col_no += 1
1582
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1583

    
1584
                                        row_no += 1
1585
                                        col_no = 24 if col_no == 27 else 28
1586
                                        ws.cell(row_no, col_no, 'Disc.P :')
1587
                                        col_no += 2
1588
                                        ws.cell(row_no, col_no, round(attr['Disc.P'], 3))
1589
                                        col_no += 1
1590
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1591

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

    
1600
                                        row_no += 1
1601
                                        col_no = 24 if col_no == 27 else 28
1602
                                        ws.cell(row_no, col_no, 'Head :')
1603
                                        col_no += 2
1604
                                        ws.cell(row_no, col_no, round(attr['Head'], 3))
1605
                                        col_no += 1
1606
                                        ws.cell(row_no, col_no, units['Length'])
1607

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

    
1616
                                        row_no += 1
1617
                                        col_no = 24 if col_no == 27 else 28
1618
                                        ws.cell(row_no, col_no, 'Vap. P :')
1619
                                        col_no += 2
1620
                                        ws.cell(row_no, col_no, attr['Vap. P'])
1621
                                        col_no += 1
1622
                                        ws.cell(row_no, col_no, '{}.a'.format(units['Pressure']))
1623

    
1624
                                        row_no += 1
1625
                                        col_no = 24 if col_no == 27 else 28
1626
                                        ws.cell(row_no, col_no, 'HHP :')
1627
                                        col_no += 2
1628
                                        ws.cell(row_no, col_no, round(attr['HHP'], 3))
1629
                                        col_no += 1
1630
                                        ws.cell(row_no, col_no, units['Power'])
1631

    
1632
                                        col_no = 28 if col_no == 27 else 24
1633
                                        row_no = row_no - 7 if col_no == 28 else row_no + 2
1634

    
1635
                        start_row_no += math.ceil(len(pumps)) * 9
1636

    
1637
                        # Compressor
1638
                        compressors = []
1639
                        row_no = start_row_no
1640
                        col_no = start_col_no
1641
                        for loop in loops:
1642
                            for item in loop.items:
1643
                                parent = item.parent
1644
                                if parent:
1645
                                    name = str(item).replace(
1646
                                        '_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
1647
                                    if name in compressors:
1648
                                        continue
1649
                                    if name[:3] == 'R_K' or name[:3] == 'L_K':
1650
                                        compressors.append(name)
1651

    
1652
                                        attr = item.parent.attribute
1653
                                        ws.cell(row_no, col_no, attr['Tag_No']).font = Font(bold=True,
1654
                                                                                            underline='single',
1655
                                                                                            size=11)
1656

    
1657
                                        row_no += 1
1658
                                        ws.cell(row_no, col_no, 'Suct.P :')
1659
                                        col_no += 2
1660
                                        ws.cell(row_no, col_no, round(attr['Suct.P'], 3))
1661
                                        col_no += 1
1662
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1663

    
1664
                                        row_no += 1
1665
                                        col_no = 24 if col_no == 27 else 28
1666
                                        ws.cell(row_no, col_no, 'Disc.P :')
1667
                                        col_no += 2
1668
                                        ws.cell(row_no, col_no, round(attr['Disc.P'], 3))
1669
                                        col_no += 1
1670
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1671

    
1672
                                        row_no += 1
1673
                                        col_no = 24 if col_no == 27 else 28
1674
                                        ws.cell(row_no, col_no, 'Diff.P :')
1675
                                        col_no += 2
1676
                                        ws.cell(row_no, col_no, round(attr['Diff.P'], 3))
1677
                                        col_no += 1
1678
                                        ws.cell(row_no, col_no, units['Pressure'])
1679

    
1680
                                        row_no += 1
1681
                                        col_no = 24 if col_no == 27 else 28
1682
                                        ws.cell(row_no, col_no, 'HHP :')
1683
                                        col_no += 2
1684
                                        ws.cell(row_no, col_no, round(attr['HHP'], 3))
1685
                                        col_no += 1
1686
                                        ws.cell(row_no, col_no, units['Power'])
1687

    
1688
                                        col_no = 28 if col_no == 27 else 24
1689
                                        row_no = row_no - 4 if col_no == 28 else row_no + 2
1690

    
1691
                        start_row_no += math.ceil(len(compressors)) * 9
1692

    
1693
                        # Control Valve
1694
                        control_valves = []
1695
                        row_no = start_row_no
1696
                        col_no = start_col_no
1697
                        for loop in loops:
1698
                            for item in loop.items:
1699
                                parent = item.parent
1700
                                if parent:
1701
                                    name = str(item).replace(
1702
                                        '_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
1703
                                    if name in control_valves:
1704
                                        continue
1705
                                    if name[:3] == 'CV_':
1706
                                        control_valves.append(name)
1707

    
1708
                                        attr = item.parent.attribute
1709
                                        if 'Tag_No' in attr:
1710
                                            ws.cell(row_no, col_no, attr['Tag_No']).font = Font(bold=True,
1711
                                                                                                underline='single',
1712
                                                                                                size=11)
1713

    
1714
                                        row_no += 1
1715
                                        ws.cell(row_no, col_no, 'Inlet P :')
1716
                                        col_no += 2
1717
                                        if 'Suct.P' in attr:
1718
                                            ws.cell(row_no, col_no, round(attr['Suct.P'], 3))
1719
                                        col_no += 1
1720
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1721

    
1722
                                        row_no += 1
1723
                                        col_no = 24 if col_no == 27 else 28
1724
                                        ws.cell(row_no, col_no, 'Outlet P :')
1725
                                        col_no += 2
1726
                                        if 'Disc.P' in attr:
1727
                                            ws.cell(row_no, col_no, round(attr['Disc.P'], 3))
1728
                                        col_no += 1
1729
                                        ws.cell(row_no, col_no, '{}.g'.format(units['Pressure']))
1730

    
1731
                                        row_no += 1
1732
                                        col_no = 24 if col_no == 27 else 28
1733
                                        ws.cell(row_no, col_no, 'Diff.P :')
1734
                                        col_no += 2
1735
                                        if 'Diff.P' in attr:
1736
                                            ws.cell(row_no, col_no, round(attr['Diff.P'], 3))
1737
                                        col_no += 1
1738
                                        ws.cell(row_no, col_no, units['Pressure'])
1739

    
1740
                                        row_no += 1
1741
                                        col_no = 24 if col_no == 27 else 28
1742
                                        ws.cell(row_no, col_no, 'dP Ratio :')
1743
                                        col_no += 2
1744
                                        if 'dP Ratio' in attr:
1745
                                            ws.cell(row_no, col_no, round(attr['dP Ratio'] * 100, 2))
1746
                                        col_no += 1
1747
                                        ws.cell(row_no, col_no, '%')
1748

    
1749
                                        col_no = 28 if col_no == 27 else 24
1750
                                        row_no = row_no - 4 if col_no == 28 else row_no + 2
1751

    
1752
                        # write hmb unit
1753
                        ws['B55'].value = '-'
1754
                        ws['B56'].value = units['Pipe_Diameter']
1755
                        ws['B57'].value = units['Flowrate_Mass']
1756
                        ws['B58'].value = units['Flowrate_Volume']
1757
                        ws['B59'].value = units['Density']
1758
                        ws['B60'].value = units['Viscosity']
1759
                        ws['B61'].value = units['Temperature']  # Hidden
1760
                        ws['B62'].value = ''  # Hidden
1761
                        ws['B63'].value = ''  # Hidden
1762
                        ws['B64'].value = ''  # Hidden
1763
                        ws['B65'].value = units['Pipe_Diameter']  # Hidden
1764
                        ws['B66'].value = units['Pipe_Diameter']  # Hidden
1765
                        ws['B67'].value = ''  # Hidden
1766
                        ws['B68'].value = units['Length']
1767
                        ws['B69'].value = units['Length']
1768
                        ws['B70'].value = units['Roughness']
1769
                        ws['B71'].value = units['Velocity']  # Hidden
1770
                        ws['B72'].value = '{}/100{})'.format(units['Pressure'], units['Length'])  # Hidden
1771
                        ws['B74'].value = units['Velocity']
1772
                        ws['B75'].value = ''  # Hidden
1773
                        ws['B76'].value = ''  # Hidden
1774
                        ws['B77'].value = '{}/100{}'.format(units['Pressure'], units['Length'])
1775
                        ws['B78'].value = units['Pressure']
1776
                        ws['B79'].value = units['Pressure']
1777
                        ws['B80'].value = '{}(g)'.format(units['Pressure'])
1778

    
1779
                        page_no += 1
1780

    
1781
                    current_ws = wb.get_sheet_by_name('Page (1)')
1782
                    # write hmb data
1783
                    drawing = app_doc_data.activeDrawing
1784
                    hmbs = drawing.hmbTable._hmbs
1785
                    if hmbs is not None:
1786
                        stream_count = 1
1787
                        col_no = 3
1788
                        _hmbs = sorted(hmbs, key=lambda hmb: hmb.stream_no)
1789
                        for hmb in _hmbs:
1790
                            if hmb.isDeleted == True:
1791
                                continue
1792

    
1793
                            ws = self.get_work_sheet(wb, stream_count)
1794
                            if ws != current_ws:
1795
                                current_ws = ws
1796
                                col_no = 3
1797

    
1798
                            row_no = 54
1799
                            ws.cell(row_no, col_no, hmb.stream_no)
1800
                            row_no += 1
1801
                            ws.cell(row_no, col_no, hmb.phase_type)
1802
                            row_no += 1
1803
                            ws.cell(row_no, col_no, hmb.inside_pipe_size)
1804
                            row_no += 1
1805
                            ws.cell(row_no, col_no, hmb.flowrate_mass)
1806
                            row_no += 1
1807
                            ws.cell(row_no, col_no, hmb.flowrate_volume)
1808
                            row_no += 1
1809
                            ws.cell(row_no, col_no, hmb.density)
1810
                            row_no += 1
1811
                            ws.cell(row_no, col_no, hmb.viscosity)
1812
                            row_no += 1
1813
                            ws.cell(row_no, col_no, hmb.temperature)
1814
                            row_no += 1
1815
                            ws.cell(row_no, col_no, hmb.molecular_weight)
1816
                            row_no += 1
1817
                            ws.cell(row_no, col_no, hmb.specific_heat_ratio)
1818
                            row_no += 1
1819
                            ws.cell(row_no, col_no, hmb.compress_factor)
1820
                            row_no += 1
1821
                            ws.cell(row_no, col_no, hmb.nominal_pipe_size)
1822
                            row_no += 1
1823
                            ws.cell(row_no, col_no, hmb.inside_pipe_size)
1824
                            row_no += 1
1825
                            ws.cell(row_no, col_no, hmb.schedule_no)
1826
                            row_no += 1
1827
                            ws.cell(row_no, col_no, hmb.straight_length)
1828
                            row_no += 1
1829
                            ws.cell(row_no, col_no, hmb.equivalent_length)
1830
                            row_no += 1
1831
                            ws.cell(row_no, col_no, hmb.roughness)
1832
                            row_no += 1
1833
                            ws.cell(row_no, col_no, hmb.limitation_velocity)
1834
                            row_no += 1
1835
                            ws.cell(row_no, col_no, hmb.limitation_pressure_drop)
1836
                            row_no += 2
1837
                            ws.cell(row_no, col_no, hmb.velocity)
1838
                            row_no += 1
1839
                            ws.cell(row_no, col_no, hmb.reynolds)
1840
                            row_no += 1
1841
                            ws.cell(row_no, col_no, hmb.friction_factor)
1842
                            row_no += 1
1843
                            ws.cell(row_no, col_no, hmb.pressure_drop)
1844
                            row_no += 1
1845
                            ws.cell(row_no, col_no, hmb.pressure_drop_friction)
1846
                            row_no += 1
1847
                            ws.cell(row_no, col_no, hmb.pressure_drop_static)
1848
                            row_no += 1
1849
                            ws.cell(row_no, col_no, hmb.pressure_pipe_end_point)
1850

    
1851
                            col_no += 1
1852
                            stream_count += 1
1853

    
1854
                    # two_phase
1855
                    if hmbs is not None:
1856
                        for hmb in hmbs:
1857
                            if hmb.phase_type == 'Mixed':
1858
                                lines = [item for item in self.graphicsView.scene.items() if type(
1859
                                    item) is QEngineeringStreamlineItem and str(item.uid) == str(hmb.components_uid)]
1860
                                if lines:
1861
                                    ws = wb.copy_worksheet(wb.get_sheet_by_name('Two_phase'))
1862
                                    ws.title = 'Two Phase {}'.format(str(lines[0]))
1863

    
1864
                                    # Information
1865
                                    ws['B5'].value = 'File : Two_Phase'
1866
                                    ws['B6'].value = 'Line : {}'.format(lines[0].stream_no)
1867
                                    ws['G6'].value = 'Made by : {}'.format(configs[0].value)
1868

    
1869
                                    # Process Data
1870
                                    ws['C10'].value = units['Flowrate_Mass']
1871
                                    ws['D10'].value = units['Density']
1872
                                    ws['E10'].value = units['Viscosity']
1873
                                    ws['F10'].value = '{}(g)'.format(units['Pressure'])
1874
                                    ws['G10'].value = units['Temperature']
1875

    
1876
                                    ws['C11'].value = lines[0].data.liquid_flowrate_mass
1877
                                    ws['D11'].value = lines[0].data.liquid_density
1878
                                    ws['E11'].value = lines[0].data.liquid_viscosity
1879

    
1880
                                    ws['C12'].value = lines[0].data.vapor_flowrate_mass
1881
                                    ws['D12'].value = lines[0].data.vapor_density
1882
                                    ws['E12'].value = lines[0].data.vapor_viscosity
1883
                                    ws['F12'].value = lines[0].data.vapor_pressure
1884
                                    ws['G12'].value = lines[0].data.vapor_temperature
1885
                                    ws['H12'].value = lines[0].data.vapor_molecular_weight
1886
                                    ws['I12'].value = lines[0].data.vapor_compress_factor
1887

    
1888
                                    # Geometry Data
1889
                                    ws['D16'].value = units['Pipe_Diameter']
1890
                                    ws['E16'].value = units['Roughness']
1891
                                    ws['F16'].value = units['Length']
1892

    
1893
                                    row_no = 17
1894
                                    for geometry in lines[0].mixed_geometry:
1895
                                        col_no = 3
1896
                                        ws.cell(row_no, col_no, str(geometry[0]))  # Element
1897
                                        col_no += 1
1898
                                        ws.cell(row_no, col_no,
1899
                                                float(geometry[3]) if is_float(geometry[3]) else None)  # ID
1900
                                        col_no += 1
1901
                                        ws.cell(row_no, col_no,
1902
                                                float(geometry[4]) if is_float(geometry[4]) else None)  # Roughness
1903
                                        col_no += 1
1904
                                        ws.cell(row_no, col_no,
1905
                                                float(geometry[5]) if is_float(geometry[5]) else None)  # Length
1906
                                        col_no += 1
1907
                                        ws.cell(row_no, col_no,
1908
                                                float(geometry[6]) if is_float(geometry[6]) else None)  # Angle
1909
                                        col_no += 1
1910
                                        ws.cell(row_no, col_no,
1911
                                                float(geometry[7]) if is_float(geometry[7]) else None)  # R/D
1912
                                        col_no += 1
1913
                                        ws.cell(row_no, col_no,
1914
                                                float(geometry[9]) if is_float(geometry[9]) else None)  # K
1915

    
1916
                                        row_no += 1
1917

    
1918

    
1919
                                    # horizontal_flow_image = openpyxl.drawing.image.Image(horizontal_flow_image_path)
1920
                                    # horizontal_flow_image.width *= 0.6
1921
                                    # horizontal_flow_image.height *= 0.6
1922
                                    # ws.add_image(horizontal_flow_image, 'L3')
1923

    
1924
                                    # vertical_upward_flow_image = openpyxl.drawing.image.Image(
1925
                                    #     vertical_upward_flow_image_path)
1926
                                    # vertical_upward_flow_image.width *= 0.6
1927
                                    # vertical_upward_flow_image.height *= 0.6
1928
                                    # ws.add_image(vertical_upward_flow_image, 'R3')
1929

    
1930
                                    # vertical_downward_flow_image = openpyxl.drawing.image.Image(
1931
                                    #     vertical_downward_flow_image_path)
1932
                                    # vertical_downward_flow_image.width *= 0.6
1933
                                    # vertical_downward_flow_image.height *= 0.6
1934
                                    # ws.add_image(vertical_downward_flow_image, 'Z3')
1935

    
1936
                                    horizontal = DrawImage(horizontal_flow_image_path)
1937
                                    vertical_upward = DrawImage(vertical_upward_flow_image_path)
1938
                                    vertical_downward = DrawImage(vertical_downward_flow_image_path)
1939

    
1940
                                    # Calculation Result
1941
                                    MainWindow.write_calculation_result_units(ws, units)
1942

    
1943
                                    row_no = 37
1944
                                    for row in lines[0].mixed_pressure_variation:
1945
                                        col_no = 12
1946
                                        ws.cell(row_no, col_no, str(row[0]))    # Element
1947
                                        col_no += 1
1948
                                        ws.cell(row_no, col_no, row[1])         # ID
1949
                                        col_no += 1
1950
                                        ws.cell(row_no, col_no, row[2])         # Length
1951
                                        col_no += 1
1952
                                        ws.cell(row_no, col_no, row[3])         # Angle
1953
                                        col_no += 1
1954
                                        ws.cell(row_no, col_no, row[4])         # K
1955
                                        col_no += 1
1956
                                        ws.cell(row_no, col_no, row[5])         # Pressure
1957
                                        col_no += 1
1958
                                        ws.cell(row_no, col_no, row[6])         # Void
1959
                                        col_no += 1
1960
                                        ws.cell(row_no, col_no, row[7])         # Quality
1961
                                        col_no += 1
1962
                                        ws.cell(row_no, col_no, row[8])         # Mean. Density
1963
                                        col_no += 1
1964
                                        ws.cell(row_no, col_no, row[9])         # Homo. Density
1965
                                        col_no += 1
1966
                                        ws.cell(row_no, col_no, row[10])        # V.Density
1967
                                        col_no += 1
1968
                                        ws.cell(row_no, col_no, row[11])        # Mean.Vel
1969
                                        col_no += 1
1970
                                        ws.cell(row_no, col_no, row[12])        # Homo.Vel
1971
                                        col_no += 1
1972
                                        ws.cell(row_no, col_no, row[13])        # Max.Vel
1973
                                        col_no += 1
1974
                                        ws.cell(row_no, col_no, row[14])        # Ero.Vel
1975
                                        col_no += 1
1976
                                        ws.cell(row_no, col_no, row[15])        # Pattern X
1977
                                        col_no += 1
1978
                                        ws.cell(row_no, col_no, row[16])        # Pattern Y
1979
                                        col_no += 1
1980
                                        ws.cell(row_no, col_no, str(row[17]) if row[17] else '')    # Regime
1981
                                        col_no += 1
1982
                                        ws.cell(row_no, col_no, row[18])        # Friction
1983
                                        col_no += 1
1984
                                        ws.cell(row_no, col_no, row[19])        # Gravity
1985
                                        col_no += 1
1986
                                        ws.cell(row_no, col_no, row[20])        # Moment
1987
                                        col_no += 1
1988
                                        ws.cell(row_no, col_no, row[21])        # Total
1989

    
1990
                                        # load_regime
1991
                                        length = row[2]
1992
                                        if length is not None:
1993
                                            x = float(row[15])
1994
                                            y = float(row[16])
1995

    
1996
                                            angle = float(row[3])
1997
                                            if angle == 0:
1998
                                                # Horizontal
1999
                                                origin_x = 57
2000
                                                origin_y = 652
2001

    
2002
                                                image_min_x = 57
2003
                                                image_min_y = 2
2004
                                                image_max_x = 603
2005
                                                image_max_y = 651
2006
                                                left = origin_x + ((image_max_x - image_min_x) / (7 - (-5))) * (
2007
                                                            math.log10(x) - (-5))
2008
                                                top = origin_y - ((image_max_y - image_min_y) / (5 - (-9))) * (
2009
                                                            math.log10(y) - (-9))
2010

    
2011
                                                horizontal.draw_regime(left, top, 8)
2012
                                                # horizontal.draw_regime(origin_x, origin_y, 8) # 0, 0 위치 확인 방법
2013
                                            elif angle > 0:
2014
                                                # Vertical Upward
2015
                                                origin_x = 59
2016
                                                origin_y = 681
2017

    
2018
                                                image_min_x = 60
2019
                                                image_min_y = 29
2020
                                                image_max_x = 596
2021
                                                image_max_y = 681
2022
                                                left = origin_x + ((image_max_x - image_min_x) / (7 - (-2))) * (
2023
                                                            math.log10(x) - (-2))
2024
                                                top = origin_y - ((image_max_y - image_min_y) / (8 - (-5))) * (
2025
                                                            math.log10(y) - (-5))
2026

    
2027
                                                vertical_upward.draw_regime(left, top, 8)
2028
                                                # vertical_upward.draw_regime(origin_x, origin_y, 8) # 0, 0 위치 확인 방법
2029
                                            elif angle < 0:
2030
                                                # Vertical Downward
2031
                                                origin_x = 55
2032
                                                origin_y = 656
2033

    
2034
                                                image_min_x = 55
2035
                                                image_min_y = 21
2036
                                                image_max_x = 572
2037
                                                image_max_y = 656
2038
                                                left = origin_x + ((image_max_x - image_min_x) / (20 - 0)) * (x - 0)
2039
                                                top = origin_y - ((image_max_y - image_min_y) / (24 - 0)) * (y - 0)
2040

    
2041
                                                vertical_downward.draw_regime(left, top, 8)
2042
                                                # vertical_downward.draw_regime(origin_x, origin_y, 8) # 0, 0 위치 확인 방법
2043

    
2044
                                        row_no += 1
2045

    
2046
                                    with NamedTemporaryFile() as f:
2047
                                        horizontal_image_path = f.name + '.png'
2048
                                    horizontal.save_as(horizontal_image_path)
2049

    
2050
                                    horizontal_flow_image = openpyxl.drawing.image.Image(horizontal_image_path)
2051
                                    horizontal_flow_image.width *= 0.6
2052
                                    horizontal_flow_image.height *= 0.6
2053
                                    ws.add_image(horizontal_flow_image, 'L4')
2054

    
2055
                                    with NamedTemporaryFile() as f:
2056
                                        vertical_upward_image_path = f.name + '.png'
2057
                                    vertical_upward.save_as(vertical_upward_image_path)
2058

    
2059
                                    vertical_upward_flow_image = openpyxl.drawing.image.Image(
2060
                                        vertical_upward_image_path)
2061
                                    vertical_upward_flow_image.width *= 0.6
2062
                                    vertical_upward_flow_image.height *= 0.6
2063
                                    ws.add_image(vertical_upward_flow_image, 'S3')
2064

    
2065
                                    with NamedTemporaryFile() as f:
2066
                                        vertical_downward_image_path = f.name + '.png'
2067
                                    vertical_downward.save_as(vertical_downward_image_path)
2068

    
2069
                                    vertical_downward_flow_image = openpyxl.drawing.image.Image(
2070
                                        vertical_downward_image_path)
2071
                                    vertical_downward_flow_image.width *= 0.6
2072
                                    vertical_downward_flow_image.height *= 0.6
2073
                                    ws.add_image(vertical_downward_flow_image, 'AA3')
2074

    
2075
                    active_sheet = wb.get_sheet_by_name('Page (1)')
2076
                    if active_sheet:
2077
                        wb.active = active_sheet
2078

    
2079
                    wb.get_sheet_by_name('Page').sheet_state = 'hidden'
2080
                    wb.get_sheet_by_name('Two_phase').sheet_state = 'hidden'
2081

    
2082
                    name_without_ext, ext = os.path.splitext(file_name)
2083
                    save_file_name = file_name if ext.upper() == '.XLSX' else name_without_ext + '.xlsx'
2084
                    wb.save(filename=save_file_name)
2085

    
2086
                    os.startfile(save_file_name)
2087

    
2088
                    QMessageBox.information(self, self.tr('Information'), self.tr('Report is done'))
2089
        except Exception as ex:
2090
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2091
                      f"{sys.exc_info()[-1].tb_lineno}"
2092
            self.addMessage.emit(MessageType.Error, message)
2093

    
2094
    @staticmethod
2095
    def write_calculation_result_units(ws, units) -> None:
2096
        """write calculation result unit for 2-phase"""
2097

    
2098
        row_no = 36
2099
        ws.cell(row_no, 13, units['Pipe_Diameter'])  # ID
2100
        ws.cell(row_no, 14, units['Length'])  # Length
2101
        ws.cell(row_no, 17, units['Pressure'])  # Pressure
2102
        ws.cell(row_no, 20, units['Density'])  # Mean. Density
2103
        ws.cell(row_no, 21, units['Density'])  # Homo. Density
2104
        ws.cell(row_no, 22, units['Density'])  # V.Density
2105
        ws.cell(row_no, 23, units['Velocity'])  # Mean.Vel
2106
        ws.cell(row_no, 24, units['Velocity'])  # Homo.Vel
2107
        ws.cell(row_no, 25, units['Velocity'])  # Max.Vel
2108
        ws.cell(row_no, 26, units['Velocity'])  # Ero.Vel
2109

    
2110
    def get_work_sheet(self, work_book, count):
2111
        page_no = math.ceil(count / 20)
2112
        return work_book.get_sheet_by_name('Page ({})'.format(page_no))
2113

    
2114
    def display_output(self):
2115
        from HydroCalculationCommand import HydroCalculationCommand
2116
        from Outputs import Output
2117

    
2118
        try:
2119
            """ display output """
2120
            app_doc_data = AppDocData.instance()
2121
            drawing = app_doc_data.activeDrawing
2122
            if drawing is None:
2123
                return
2124

    
2125
            self.clear_output()
2126
            if not HydroCalculationCommand.ERRORS:
2127
                self.tableWidgetOutput.setColumnCount(3)
2128
                self.tableWidgetOutput.horizontalHeader().setVisible(False)
2129
                self.tableWidgetOutput.verticalHeader().setVisible(False)
2130
                self.tableWidgetOutput.setSelectionMode(QAbstractItemView.SingleSelection)
2131
                self.tableWidgetOutput.setSelectionBehavior(QAbstractItemView.SelectRows)
2132
                self.tableWidgetOutput.setEditTriggers(QAbstractItemView.NoEditTriggers)
2133
                self.tableWidgetOutput.horizontalHeader().setStretchLastSection(True)
2134

    
2135
                self.tableWidgetDeviation.setColumnCount(1)
2136
                self.tableWidgetDeviation.horizontalHeader().setVisible(False)
2137
                self.tableWidgetDeviation.verticalHeader().setVisible(False)
2138
                self.tableWidgetDeviation.setSelectionMode(QAbstractItemView.SingleSelection)
2139
                self.tableWidgetDeviation.setSelectionBehavior(QAbstractItemView.SelectRows)
2140
                self.tableWidgetDeviation.setEditTriggers(QAbstractItemView.NoEditTriggers)
2141
                self.tableWidgetDeviation.horizontalHeader().setStretchLastSection(True)
2142
                self.tableWidgetDeviation.resizeRowsToContents()
2143
                self.tableWidgetDeviation.resizeColumnsToContents()
2144

    
2145
                units = [attr[1] for attr in drawing.attrs if attr[0] == 'Units'][0]
2146

    
2147
                loops = drawing.loops
2148

    
2149
                # Deviation
2150
                self.add_data(self.tableWidgetDeviation, 'Loop_Deviation', None, None, True, None, None)
2151
                strmsg = None
2152
                for loop in loops:
2153
                    deviation = loop.deviation
2154
                    if round(deviation, 9) == 0:
2155
                        continue
2156

    
2157
                    for i in range(1, len(loop.items) - 1, 3):
2158
                        if i == 1:
2159
                            loop_no = loop.name.replace('Loop', '')
2160
                            if len(loop_no) > 1:
2161
                                strmsg = '({}) {}'.format(loop_no, loop.items[i].stream_no)
2162
                            else:
2163
                                strmsg = '(0{}) {}'.format(loop_no, loop.items[i].stream_no)
2164
                        else:
2165
                            if len(str(loop.items[i - 1])) > 13 and str(loop.items[i - 1])[:13] == 'Line_Splitter':
2166
                                pre_stream_num = loop.items[i - 3].stream_no
2167
                                cur_stream_num = loop.items[i].stream_no
2168

    
2169
                                if strmsg.__contains__('-'):
2170
                                    last_stream_num = str(strmsg).rsplit('-', 1)[1]
2171
                                else:
2172
                                    last_stream_num = str(strmsg).rsplit(' ', 1)[1]
2173

    
2174
                                if pre_stream_num != int(last_stream_num):
2175
                                    strmsg = '{}-{}-{}'.format(strmsg, pre_stream_num, cur_stream_num)
2176

    
2177
                    last_stream_num = loop.items[len(loop.items) - 2].stream_no
2178
                    strmsg = '{}-{}'.format(strmsg, last_stream_num)
2179

    
2180
                    if round(deviation, 3) < 0:
2181
                        self.add_data(self.tableWidgetDeviation,
2182
                                      '{} = {} {}'.format(strmsg, round(deviation, 3), units['Pressure']), None, None, None,
2183
                                      None, QBrush(QColor(255, 0, 0)))
2184
                    else:
2185
                        self.add_data(self.tableWidgetDeviation,
2186
                                      '{} = {} {}'.format(strmsg, round(deviation, 3), units['Pressure']), None, None, None,
2187
                                      None)
2188

    
2189
                names = []
2190
                # Pump
2191
                for loop in loops:
2192
                    for item in loop.items:
2193
                        parent = item.parent
2194
                        if parent:
2195
                            name = str(item).replace('_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
2196
                            if name in names:
2197
                                continue
2198
                            if name[:3] == 'R_P' or name[:3] == 'L_P' or name[:3] == 'V_P':
2199
                                names.append(name)
2200
                                attr = item.parent.attribute
2201
                                if len(attr) > 0:
2202
                                    """store calculation output for pump"""
2203
                                    output = Output()
2204
                                    output.components_uid = str(item.parent.uid)
2205
                                    output.suctp = round(attr['Suct.P'], 3)
2206
                                    output.discp = round(attr['Disc.P'], 3)
2207
                                    output.diffp = round(attr['Diff.P'], 3)
2208
                                    output.head = round(attr['Head'], 3)
2209
                                    output.npsha = round(attr['NPSHa'], 3)
2210
                                    output.vapp = attr['Vap. P']
2211
                                    output.hhp = round(attr['HHP'], 3)
2212
                                    app_doc_data.outputs.append(output)
2213
                                    """up to here"""
2214

    
2215
                                    self.add_data(self.tableWidgetOutput, attr['Tag_No'], None, None, True, True)
2216
                                    self.add_data(self.tableWidgetOutput, 'Suct.P :', output.suctp,
2217
                                                  '{}.g'.format(units['Pressure']))
2218
                                    self.add_data(self.tableWidgetOutput, 'Disc.P :', output.discp,
2219
                                                  '{}.g'.format(units['Pressure']))
2220
                                    self.add_data(self.tableWidgetOutput, 'Diff.P :', output.diffp,
2221
                                                  units['Pressure'])
2222
                                    self.add_data(self.tableWidgetOutput, 'Head :', output.head, units['Length'])
2223
                                    self.add_data(self.tableWidgetOutput, 'NPSHa :', output.npsha, units['Length'])
2224
                                    self.add_data(self.tableWidgetOutput, 'Vap. P :', output.vapp,
2225
                                                  '{}.a'.format(units['Pressure']))
2226
                                    self.add_data(self.tableWidgetOutput, 'HHP :', output.hhp, units['Power'])
2227

    
2228

    
2229
                # Compressor
2230
                for loop in loops:
2231
                    for item in loop.items:
2232
                        parent = item.parent
2233
                        if parent:
2234
                            name = str(item).replace('_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
2235
                            if name in names:
2236
                                continue
2237
                            if name[:3] == 'R_K' or name[:3] == 'L_K':
2238
                                names.append(name)
2239

    
2240
                                attr = item.parent.attribute
2241
                                if len(attr) > 0:
2242
                                    """store calculation output for compressor"""
2243
                                    output = Output()
2244
                                    output.components_uid = str(item.parent.uid)
2245
                                    output.suctp = round(attr['Suct.P'], 3)
2246
                                    output.discp = round(attr['Disc.P'], 3)
2247
                                    output.diffp = round(attr['Diff.P'], 3)
2248
                                    output.hhp = round(attr['HHP'], 3)
2249
                                    app_doc_data.outputs.append(output)
2250
                                    """up to here"""
2251

    
2252
                                    self.add_data(self.tableWidgetOutput, attr['Tag_No'], None, None, True, True)
2253
                                    self.add_data(self.tableWidgetOutput, 'Suct.P :', output.suctp,
2254
                                                  '{}.g'.format(units['Pressure']))
2255
                                    self.add_data(self.tableWidgetOutput, 'Disc.P :', output.discp,
2256
                                                  '{}.g'.format(units['Pressure']))
2257
                                    self.add_data(self.tableWidgetOutput, 'Diff.P :', output.diffp,
2258
                                                  units['Pressure'])
2259
                                    self.add_data(self.tableWidgetOutput, 'HHP :', output.hhp, units['Power'])
2260

    
2261
                # Control Valve
2262
                for loop in loops:
2263
                    for item in loop.items:
2264
                        parent = item.parent
2265
                        if parent:
2266
                            name = str(item).replace('_{}'.format(str(item).split('_')[len(str(item).split('_')) - 1]), '')
2267
                            if name in names:
2268
                                continue
2269
                            if name[:3] == 'CV_':
2270
                                names.append(name)
2271

    
2272
                                attr = item.parent.attribute
2273
                                if len(attr) > 0:
2274
                                    """store calculation output for control valve"""
2275
                                    output = Output()
2276
                                    output.components_uid = str(item.parent.uid)
2277
                                    output.suctp = round(attr['Suct.P'], 3)
2278
                                    output.discp = round(attr['Disc.P'], 3)
2279
                                    output.diffp = round(attr['Diff.P'], 3)
2280
                                    output.dpratio = round(attr['dP Ratio'] * 100, 2)
2281
                                    app_doc_data.outputs.append(output)
2282
                                    """up to here"""
2283

    
2284
                                    self.add_data(self.tableWidgetOutput, attr['Tag_No'], None, None, True, True)
2285
                                    self.add_data(self.tableWidgetOutput, 'Inlet P :', output.suctp,
2286
                                                  '{}.g'.format(units['Pressure']))
2287
                                    self.add_data(self.tableWidgetOutput, 'Outlet P :', output.discp,
2288
                                                  '{}.g'.format(units['Pressure']))
2289
                                    self.add_data(self.tableWidgetOutput, 'Diff.P :', output.diffp,
2290
                                                  units['Pressure'])
2291
                                    self.add_data(self.tableWidgetOutput, 'dP Ratio :', output.dpratio, '%')
2292
        except Exception as ex:
2293
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2294
                      f"{sys.exc_info()[-1].tb_lineno}"
2295
            self.addMessage.emit(MessageType.Error, message)
2296

    
2297
    def display_loops(self):
2298
        """ display loops """
2299
        from HydroCalculationCommand import HydroCalculationCommand
2300

    
2301
        try:
2302
            drawing = AppDocData.instance().activeDrawing
2303
            if drawing is None: return
2304

    
2305
            self.tableWidgetLoop.clear()
2306

    
2307
            loops = drawing.loops
2308
            if loops and not HydroCalculationCommand.ERRORS:
2309
                self.tableWidgetLoop.setColumnCount(len(loops) * 5)
2310

    
2311
                _col_names = [[loop.name, 'pressure', 'Static\nLine\ndP_Eq', 'El.\nDensity\nEl.', 'Extra'] for loop in
2312
                              loops]
2313
                col_names = []
2314
                for col_name in _col_names: col_names.extend(col_name)
2315
                self.tableWidgetLoop.setHorizontalHeaderLabels(col_names)
2316
                self.tableWidgetLoop.horizontalHeader().setVisible(True)
2317

    
2318
                max_rows = 0
2319
                for col in range(len(loops)):
2320
                    rows = len(loops[col].items)
2321
                    max_rows = max(max_rows, rows)
2322
                    self.tableWidgetLoop.setRowCount(max_rows)
2323

    
2324
                    for row in range(len(loops[col].items)):
2325
                        item = QTableWidgetItem(str(loops[col].items[row]))
2326
                        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
2327
                        self.tableWidgetLoop.setItem(row, col * 5, item)
2328

    
2329
                    # display calculation values
2330
                    for row in range(len(loops[col].items)):
2331
                        if loops[col].items[row] in loops[col].pressures:
2332
                            pressure = loops[col].pressures[loops[col].items[row]] \
2333
                                if loops[col].pressures[loops[col].items[row]] else 0
2334
                            item = QTableWidgetItem(str(round(pressure, 8)))
2335
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2336
                            # if not loops[col].pressures[loops[col].items[row]]: item.setBackground(Qt.red)
2337
                            self.tableWidgetLoop.setItem(row, col * 5 + 1, item)
2338

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

    
2345
                        if loops[col].items[row] in loops[col].density_elevations:
2346
                            item = QTableWidgetItem(str(round(loops[col].density_elevations[loops[col].items[row]], 8)))
2347
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2348
                            # if not loops[col].density_elevations[loops[col].items[row]]: item.setBackground(Qt.red)
2349
                            self.tableWidgetLoop.setItem(row, col * 5 + 3, item)
2350

    
2351
                        if loops[col].items[row] in loops[col].extras:
2352
                            item = QTableWidgetItem(str(loops[col].extras[loops[col].items[row]]))
2353
                            item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
2354
                            # if not loops[col].density_elevations[loops[col].items[row]]: item.setBackground(Qt.red)
2355
                            self.tableWidgetLoop.setItem(row, col * 5 + 4, item)
2356

    
2357
                self.tableWidgetLoop.resizeColumnsToContents()
2358
                self.tableWidgetLoop.resizeRowsToContents()
2359
        except Exception as ex:
2360
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2361
                      f"{sys.exc_info()[-1].tb_lineno}"
2362
            self.addMessage.emit(MessageType.Error, message)
2363

    
2364
    def configuration(self):
2365
        """configuration"""
2366
        from ConfigurationDialog import QConfigurationDialog
2367
        from Calculation import Conversion
2368

    
2369
        try:
2370
            appDocData = AppDocData.instance()
2371
            if appDocData.activeDrawing is None:
2372
                self.showImageSelectionMessageBox()
2373
                return
2374

    
2375
            dlg = QConfigurationDialog(self)
2376
            if QDialog.Accepted == dlg.show_dialog():
2377
                if dlg.need_to_convert:
2378
                    Conversion(dlg.decimal_point)
2379
                    self.actionSaveCliked(show_message=False)
2380
                    self.load_HMB()
2381

    
2382
                self.reload_units()
2383
                self.update_label_contents()
2384
                self.change_output_font_color()
2385
        except Exception as ex:
2386
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2387
                                                           sys.exc_info()[-1].tb_lineno)
2388
            self.addMessage.emit(MessageType.Error, message)
2389

    
2390
    def showImageSelectionMessageBox(self):
2391
        """Show Image Selection Guide MessageBox"""
2392
        QMessageBox.information(self, self.tr("Information"), self.tr("First of all, please open a drawing"))
2393

    
2394
    def display_colors(self, value):
2395
        """ display colors """
2396
        from DisplayColors import DisplayColors
2397
        from DisplayColors import DisplayOptions
2398

    
2399
        DisplayColors.instance().option = DisplayOptions.DisplayByLineNo if value == True else DisplayOptions.DisplayByLineType
2400
        if hasattr(self, 'graphicsView'):
2401
            self.graphicsView.scene.update(self.graphicsView.sceneRect())
2402
            DisplayColors.instance().save_data()
2403

    
2404
    '''
2405
        @brief      Open Border file 
2406
        @author     yeonjin
2407
        @date       2019.07.10
2408
    '''
2409

    
2410
    def open_border_file(self):
2411
        self.fitWindow(None)
2412

    
2413
    def patch_data(self):
2414
        '''apply path data'''
2415
        try:
2416
            app_doc_data = AppDocData.instance()
2417
            file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Scripts', 'patch.sql')
2418
            if os.path.isfile(file_path):
2419
                with open(file_path, 'rb') as file:
2420
                    scripts = file.read().decode('utf-8')
2421
                    app_doc_data.apply_path_data(scripts)
2422

    
2423
            find_column_names = ['Homo_Den', 'Homo_Vel']
2424
            column_names = app_doc_data.get_column_names('PressureVariation')
2425
            for column_name in find_column_names:
2426
                if column_name not in column_names:
2427
                    app_doc_data.add_column(column_name, 'REAL', 'PressureVariation')
2428

    
2429
            find_column_names = ['Input_Flowrate_Type']
2430
            column_names = app_doc_data.get_column_names('HMB')
2431
            for column_name in find_column_names:
2432
                if column_name not in column_names:
2433
                    app_doc_data.add_column(column_name, 'TEXT', 'HMB')
2434

    
2435
        except Exception as ex:
2436
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
2437
                      f"{sys.exc_info()[-1].tb_lineno}"
2438
            self.addMessage.emit(MessageType.Error, message)
2439

    
2440
    def setAttributes(self, drawing):
2441
        drawing.setAttributes()
2442

    
2443
    '''
2444
        @brief      Reload HMB Units 
2445
        @author     yeonjin
2446
        @date       2019.07.10
2447
    '''
2448
    def reload_units(self):
2449
        from Drawing import Drawing
2450

    
2451
        try:
2452
            app_doc_data = AppDocData.instance()
2453
            drawing = app_doc_data.activeDrawing
2454

    
2455
            self.setAttributes(drawing)
2456

    
2457
            columnInfos = app_doc_data.getHMBDisplayNameAndUnitsExpression()
2458

    
2459
            rowIndex = 0
2460
            for row in range(columnInfos.rowCount()):
2461
                name = columnInfos.item(row, 0).text()
2462
                unit = self.convertToUnits(columnInfos.item(row, 1).text())
2463

    
2464
                self.tableWidgetHMB.setItem(rowIndex, 0,
2465
                                            set_item_properties(name, Qt.AlignLeft | Qt.AlignVCenter,
2466
                                                                QColor(51, 153, 102)))
2467
                # QColor(230, 230, 230)))
2468
                self.tableWidgetHMB.setItem(rowIndex, 1,
2469
                                            set_item_properties(unit, Qt.AlignHCenter | Qt.AlignVCenter,
2470
                                                                QColor(204, 255, 204)))
2471
                # QColor(230, 230, 230)))
2472

    
2473
                rowIndex += 1
2474

    
2475
            self.tableWidgetHMB.resizeColumnsToContents()
2476
            self.tableWidgetHMB.resizeRowsToContents()
2477

    
2478
        except Exception as ex:
2479
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2480
                      f'{sys.exc_info()[-1].tb_lineno}'
2481
            self.addMessage.emit(MessageType.Error, message)
2482

    
2483
    def load_data(self, drawing):
2484
        """ load data from drawing """
2485
        from AppDocData import AppDocData
2486
        from Calculation import Calculation
2487
        from HydroCalculationCommand import HydroCalculationCommand
2488
        from Drawing import Drawing
2489

    
2490
        try:
2491
            app_doc_data = AppDocData.instance()
2492

    
2493
            self.graphicsView.scene.clear()
2494
            # Set appDocData
2495
            app_doc_data.clear()
2496
            self.onCommandRejected()
2497
            app_doc_data.activeDrawing = drawing
2498

    
2499
            app_doc_data.build_drawing_database(drawing.path)
2500
            self.patch_data()
2501

    
2502
            self.setAttributes(drawing)
2503
            self.setMainWindowTitle(drawing.path)
2504
            self.initTableWidgetHMB()
2505
            self.clear_loop()
2506
            self.clearlogs()
2507
            self.clear_output()
2508
            # Load data on database
2509

    
2510
            self.symbolTreeWidget.initSymbolTreeWidget()
2511

    
2512
            components = app_doc_data.getComponentListByDrawingUID()
2513
            count = len(components)
2514

    
2515
            if count > 0:
2516
                try:
2517
                    self.progress = QProgressDialog(self.tr("Please wait for a while"), self.tr("Cancel"), 0, 100,
2518
                                                    self) if not hasattr(self, 'progress') else self.progress
2519
                    self.progress.setWindowModality(Qt.WindowModal)
2520
                    self.progress.setAutoReset(True)
2521
                    self.progress.setAutoClose(True)
2522
                    self.progress.setMinimum(0)
2523
                    self.progress.resize(600, 100)
2524
                    self.progress.setWindowTitle(self.tr("Load data..."))
2525
                    self.progress.show()
2526

    
2527
                    self.load_components(components)
2528

    
2529
                    """ TODO: 확인 필요
2530
                    # Loops 와 Output 을 만들기 위해 Calculation 을 한다
2531
                    for hmb in app_doc_data.activeDrawing.hmbTable._hmbs:
2532
                        if hmb.phase_type:
2533
                            Calculation(hmb)
2534
                    cmd = HydroCalculationCommand(self.graphicsView)
2535
                    cmd.execute(None)
2536
                    cmd.execute_second(None)
2537

2538
                    app_doc_data.activeDrawing.loops = cmd.loops
2539

2540
                    self.display_loops()
2541
                    self.display_output()
2542

2543
                    self.load_HMB()
2544
                    """
2545
                    self.calculation()
2546
                finally:
2547
                    self.progress.setValue(self.progress.maximum())
2548
                    self.progress.hide()
2549

    
2550
            # self.changeViewCheckedState(False)
2551
            palette = self.graphicsView.palette()
2552
            _pixmap = QPixmap(':/images/error.svg')
2553
            palette.setBrush(QPalette.Background, QBrush(_pixmap))
2554
            self.graphicsView.setPalette(palette)
2555

    
2556
        except Exception as ex:
2557
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2558
                                                           sys.exc_info()[-1].tb_lineno)
2559
            self.addMessage.emit(MessageType.Error, message)
2560

    
2561
    def saveAs_drawing(self, sourceDb):
2562
        import uuid
2563
        from shutil import copyfile
2564
        from datetime import datetime
2565
        from Drawing import Drawing
2566

    
2567
        workspace = self.get_work_space()
2568

    
2569
        options = QFileDialog.Options()
2570
        options |= QFileDialog.DontUseNativeDialog
2571
        name, _ = QFileDialog.getSaveFileName(self, self.tr('Save As'), workspace, 'HYTOS Files (*.hytos)',
2572
                                              options=options)
2573
        if name:
2574
            if os.path.splitext(name)[1] != '.hytos': name += '.hytos'
2575

    
2576
            app_doc_data = AppDocData.instance()
2577
            # copy template.db to name
2578
            copyfile(sourceDb, name)
2579

    
2580
            matches = [drawing for drawing in app_doc_data.getDrawings() if
2581
                       os.path.exists(drawing.path) and os.path.samefile(drawing.path, name)]
2582
            if not matches:
2583
                drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2584
                app_doc_data.saveDrawing(drawing)
2585
            else:
2586
                drawing = Drawing(str(matches[0].UID), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2587
                app_doc_data.updateDrawing(drawing)
2588

    
2589
            self.load_drawing_list()
2590
            self.load_data(drawing)
2591
            self.open_border_file()
2592

    
2593
    '''
2594
        @brief      create new drawing
2595
        @author     yeonjin
2596
        @date       2019.07.03
2597
    '''
2598

    
2599
    def new_drawing(self):
2600
        import uuid
2601
        from shutil import copyfile
2602
        from datetime import datetime
2603
        from Drawing import Drawing
2604

    
2605
        self.save_drawing_if_necessary()
2606

    
2607
        workspace = self.get_work_space()
2608

    
2609
        options = QFileDialog.Options()
2610
        options |= QFileDialog.DontUseNativeDialog
2611
        name, _ = QFileDialog.getSaveFileName(self, self.tr('New'), workspace, 'HYTOS Files (*.hytos)', options=options)
2612
        if name:
2613
            if os.path.splitext(name)[1] != '.hytos': name += '.hytos'
2614

    
2615
            app_doc_data = AppDocData.instance()
2616
            # copy template.db to name
2617
            copyfile(app_doc_data.getTemplateDbPath(), name)
2618

    
2619
            matches = [drawing for drawing in app_doc_data.getDrawings() if
2620
                       os.path.exists(drawing.path) and os.path.samefile(drawing.path, name)]
2621
            if not matches:
2622
                drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2623
                app_doc_data.saveDrawing(drawing)
2624
            else:
2625
                drawing = Drawing(str(matches[0].UID), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2626
                app_doc_data.updateDrawing(drawing)
2627

    
2628
            self.load_drawing_list()
2629
            self.open_border_file()
2630
            self.load_data(drawing)
2631

    
2632
    def on_open_drawing(self):
2633
        """open selected drawing by user"""
2634

    
2635
        self.save_drawing_if_necessary()
2636

    
2637
        workspace = self.get_work_space()
2638
        options = QFileDialog.Options()
2639
        options |= QFileDialog.DontUseNativeDialog
2640
        name, _ = QFileDialog.getOpenFileName(self, self.tr('Open'), workspace, 'HYTOS File(*.hytos)', options=options)
2641
        if name:
2642
            self.open_drawing(name)
2643

    
2644
    def open_drawing(self, name):
2645
        """open given drawing has name"""
2646
        import uuid
2647
        from Drawing import Drawing
2648
        from datetime import datetime
2649

    
2650
        app_doc_data = AppDocData.instance()
2651
        drawings = app_doc_data.getDrawings()
2652
        matches = [drawing for drawing in drawings if os.path.samefile(drawing.path, name)]
2653
        if not matches:
2654
            drawing = Drawing(str(uuid.uuid4()), name, str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
2655
            app_doc_data.saveDrawing(drawing)
2656

    
2657
            self.load_drawing_list()
2658
        else:
2659
            drawing = matches[0]
2660

    
2661
        # disconnect scene changed if signal is connected
2662
        if self.graphicsView.scene.receivers(self.graphicsView.scene.contents_changed) > 0:
2663
            self.graphicsView.scene.contents_changed.disconnect()
2664

    
2665
        self.load_data(drawing)
2666
        self.open_border_file()
2667

    
2668
        # connect scene changed signal
2669
        self.graphicsView.scene.contents_changed.connect(self.scene_changed)
2670

    
2671
    def get_work_space(self):
2672
        """get work space path"""
2673
        app_doc_data = AppDocData.instance()
2674

    
2675
        configs = app_doc_data.getAppConfigs('option', 'WorkSpace')
2676
        if configs and len(configs) == 1:
2677
            return configs[0].value
2678
        else:
2679
            return os.getcwd()
2680

    
2681
    def changeViewCheckedState(self, checked, clear=True):
2682
        '''change view checked state'''
2683
        if clear:
2684
            self.initTableWidgetHMB()
2685
            # self.clear_data()
2686

    
2687
    '''
2688
        @brief      create a line
2689
        @author     humkyung
2690
        @history    Jeongwoo 2018.05.10 Change method for Checkable action
2691
    '''
2692

    
2693
    def onPlaceLine(self):
2694
        self.actionLine.setChecked(True)
2695
        if not hasattr(self.actionLine, 'tag'):
2696
            self.actionLine.tag = PlaceStreamlineCommand.PlaceStreamlineCommand(self.graphicsView)
2697
            self.actionLine.tag.onSuccess.connect(self.on_stream_line_created)
2698
            self.actionLine.tag.onRejected.connect(self.onCommandRejected)
2699

    
2700
        self.graphicsView.command = self.actionLine.tag
2701

    
2702
    def add_new_stream_line(self, stream_line: QEngineeringStreamlineItem) -> None:
2703
        """add a new stream line"""
2704
        self.add_hmb_data(stream_line)
2705
        stream_line.transfer.onRemoved.connect(self.on_item_removed)
2706
        self.load_HMB()
2707

    
2708
    def on_stream_line_created(self):
2709
        """callback after stream line is created"""
2710
        from CreateCommand import CreateCommand
2711
        try:
2712
            count = len(self.actionLine.tag.streamline._vertices)
2713
            if count > 1:
2714
                stream_line = self.actionLine.tag.streamline
2715
                self.add_new_stream_line(stream_line)
2716
                self.graphicsView.scene.undo_stack.push(CreateCommand(self.graphicsView.scene, [stream_line, ]))
2717
        finally:
2718
            self.actionLine.tag.reset()
2719

    
2720
    def on_stream_line_deleted(self, stream_line):
2721
        """ callback after stream line is deleted """
2722
        app_doc_data = AppDocData.instance()
2723
        activeDrawing = app_doc_data.activeDrawing
2724
        if activeDrawing:
2725
            # activeDrawing.hmbTable.deleteByUID(stream_line.uid)
2726
            activeDrawing.hmbTable.deleteByStreamNo(stream_line.stream_no)
2727

    
2728
        self.load_HMB()
2729

    
2730
    def add_hmb_data(self, stream_line):
2731
        """ add a new hmb data associated with given stream line """
2732
        from HMBTable import HMBTable
2733
        import uuid
2734

    
2735
        try:
2736
            drawing = AppDocData.instance().activeDrawing
2737
            if drawing:
2738
                components_uid = stream_line.uid
2739
                stream_no = self.get_next_stream_no(drawing)
2740
                drawing.hmbTable.add(components_uid, stream_no)
2741
                stream_line.stream_no = stream_no
2742
                stream_line.stream_no_text = str(stream_no)
2743
        except Exception as ex:
2744
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2745
                                                           sys.exc_info()[-1].tb_lineno)
2746
            self.addMessage.emit(MessageType.Error, message)
2747

    
2748
    def get_next_stream_no(self, drawing):
2749
        if len(list(drawing.hmbTable.streamNos())) == 0:
2750
            return 1
2751
        else:
2752
            if len(list(drawing.hmbTable.deleted_stream_nos())) > 0:
2753
                deleted_stream_nos = sorted(list(drawing.hmbTable.deleted_stream_nos()))
2754
                lastStreamNo = deleted_stream_nos[-1]
2755
                return lastStreamNo
2756
            else:
2757
                streamNos = sorted(list(drawing.hmbTable.streamNos()))
2758
                lastStreamNo = streamNos[-1]
2759
                return lastStreamNo + 1
2760

    
2761
    def clear_HMB(self):
2762
        self.tableWidgetHMB.clearContents()
2763
        self.tableWidgetHMB.setColumnCount(0)
2764

    
2765
    def load_HMB(self):
2766
        """
2767
        display hmb data to table widget
2768
        @return:
2769
        """
2770
        from CalculationValidation import QCalculationValidation
2771

    
2772
        drawing = AppDocData.instance().activeDrawing
2773
        if drawing is None: return
2774

    
2775
        try:
2776
            hmbs = drawing.hmbTable._hmbs
2777
            if hmbs is not None:
2778
                model = self.tableWidgetHMB.model()
2779
                model.setColumnCount(2)
2780
                # self.tableWidgetHMB.setColumnCount(2)
2781

    
2782
                col_names = ['Stream No.\n', 'Unit\n']
2783
                _hmbs = sorted(hmbs, key=lambda hmb: hmb.stream_no)
2784
                for hmb in _hmbs:
2785
                    columnCount = model.columnCount()
2786
                    # self.tableWidgetHMB.setColumnCount(columnCount + 1)
2787
                    col_names.append(str(hmb.stream_no) + '\n(' + str(hmb.stream_no_text if hmb.stream_no_text else hmb._stream_no) + ')')
2788

    
2789
                    item = set_item_properties(hmb.uid, Qt.AlignHCenter | Qt.AlignVCenter)
2790
                    model.setItem(0, columnCount, item)
2791
                    item = set_item_properties(hmb.components_uid, Qt.AlignHCenter | Qt.AlignVCenter)
2792
                    model.setItem(1, columnCount, item)
2793
                    item = set_item_properties(hmb.stream_no, Qt.AlignHCenter | Qt.AlignVCenter)
2794
                    model.setItem(2, columnCount, item)
2795
                    item = set_item_properties(hmb.phase_type, Qt.AlignHCenter | Qt.AlignVCenter)
2796
                    item.setData(hmb.phase_type, Qt.UserRole)
2797
                    model.setItem(3, columnCount, item)
2798

    
2799
                    item = set_item_properties(convert_to_fixed_point(hmb.flowrate_mass), Qt.AlignHCenter | Qt.AlignVCenter)
2800
                    item.setData(hmb.flowrate_mass, Qt.UserRole)
2801
                    model.setItem(4, columnCount, item)
2802

    
2803
                    item = set_item_properties(convert_to_fixed_point(hmb.flowrate_volume), Qt.AlignHCenter | Qt.AlignVCenter)
2804
                    item.setData(hmb.flowrate_volume, Qt.UserRole)
2805
                    """
2806
                    if hmb.phase_type == 'Vapor':
2807
                        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
2808
                    """
2809
                    model.setItem(5, columnCount, item)
2810

    
2811
                    item = set_item_properties(convert_to_fixed_point(hmb.density), Qt.AlignHCenter | Qt.AlignVCenter)
2812
                    item.setData(hmb.density, Qt.UserRole)
2813
                    model.setItem(6, columnCount, item)
2814

    
2815
                    item = set_item_properties(convert_to_fixed_point(hmb.viscosity), Qt.AlignHCenter | Qt.AlignVCenter)
2816
                    item.setData(hmb.viscosity, Qt.UserRole)
2817
                    model.setItem(7, columnCount, item)
2818

    
2819
                    item = set_item_properties(convert_to_fixed_point(hmb.temperature), Qt.AlignHCenter | Qt.AlignVCenter)
2820
                    item.setData(hmb.temperature, Qt.UserRole)
2821
                    model.setItem(8, columnCount, item)
2822

    
2823
                    item = set_item_properties(convert_to_fixed_point(hmb.molecular_weight), Qt.AlignHCenter | Qt.AlignVCenter)
2824
                    item.setData(hmb.molecular_weight, Qt.UserRole)
2825
                    model.setItem(9, columnCount, item)
2826

    
2827
                    item = set_item_properties(convert_to_fixed_point(hmb.specific_heat_ratio), Qt.AlignHCenter | Qt.AlignVCenter)
2828
                    item.setData(hmb.specific_heat_ratio, Qt.UserRole)
2829
                    model.setItem(10, columnCount, item)
2830

    
2831
                    item = set_item_properties(convert_to_fixed_point(hmb.compress_factor), Qt.AlignHCenter | Qt.AlignVCenter)
2832
                    item.setData(hmb.compress_factor, Qt.UserRole)
2833
                    model.setItem(11, columnCount, item)
2834

    
2835
                    item = set_item_properties(convert_to_fixed_point(hmb.nominal_pipe_size), Qt.AlignHCenter | Qt.AlignVCenter)
2836
                    item.setData(hmb.nominal_pipe_size, Qt.UserRole)
2837
                    model.setItem(12, columnCount, item)
2838

    
2839
                    item = set_item_properties(convert_to_fixed_point(hmb.inside_pipe_size), Qt.AlignHCenter | Qt.AlignVCenter)
2840
                    item.setData(hmb.inside_pipe_size, Qt.UserRole)
2841
                    model.setItem(13, columnCount, item)
2842

    
2843
                    item = set_item_properties(convert_to_fixed_point(hmb.schedule_no), Qt.AlignHCenter | Qt.AlignVCenter)
2844
                    item.setData(hmb.schedule_no, Qt.UserRole)
2845
                    model.setItem(14, columnCount, item)
2846

    
2847
                    item = set_item_properties(convert_to_fixed_point(hmb.straight_length), Qt.AlignHCenter | Qt.AlignVCenter)
2848
                    item.setData(hmb.straight_length, Qt.UserRole)
2849
                    model.setItem(15, columnCount, item)
2850

    
2851
                    item = set_item_properties(convert_to_fixed_point(hmb.equivalent_length), Qt.AlignHCenter | Qt.AlignVCenter)
2852
                    item.setData(hmb.equivalent_length, Qt.UserRole)
2853
                    model.setItem(16, columnCount, item)
2854

    
2855
                    item = set_item_properties(convert_to_fixed_point(hmb.equivalent_length_input), Qt.AlignHCenter | Qt.AlignVCenter)
2856
                    item.setData(hmb.equivalent_length_input, Qt.UserRole)
2857
                    model.setItem(17, columnCount, item)
2858

    
2859
                    item = set_item_properties(convert_to_fixed_point(hmb.fitting_length), Qt.AlignHCenter | Qt.AlignVCenter)
2860
                    item.setData(hmb.fitting_length, Qt.UserRole)
2861
                    model.setItem(18, columnCount, item)
2862

    
2863
                    item = set_item_properties(convert_to_fixed_point(hmb.fitting_K), Qt.AlignHCenter | Qt.AlignVCenter)
2864
                    item.setData(hmb.fitting_K, Qt.UserRole)
2865
                    model.setItem(19, columnCount, item)
2866

    
2867
                    item = set_item_properties(convert_to_fixed_point(hmb.equivalent_length_cal), Qt.AlignHCenter | Qt.AlignVCenter)
2868
                    item.setData(hmb.equivalent_length_cal, Qt.UserRole)
2869
                    model.setItem(20, columnCount, item)
2870

    
2871
                    item = set_item_properties(convert_to_fixed_point(hmb.roughness), Qt.AlignHCenter | Qt.AlignVCenter)
2872
                    item.setData(hmb.roughness, Qt.UserRole)
2873
                    model.setItem(21, columnCount, item)
2874

    
2875
                    item = set_item_properties(convert_to_fixed_point(hmb.limitation_velocity), Qt.AlignHCenter | Qt.AlignVCenter)
2876
                    item.setData(hmb.limitation_velocity, Qt.UserRole)
2877
                    model.setItem(22, columnCount, item)
2878

    
2879
                    item = set_item_properties(convert_to_fixed_point(hmb.limitation_pressure_drop), Qt.AlignHCenter | Qt.AlignVCenter)
2880
                    item.setData(hmb.limitation_pressure_drop, Qt.UserRole)
2881
                    model.setItem(23, columnCount, item)
2882

    
2883
                    item = set_item_properties(None, Qt.AlignHCenter | Qt.AlignVCenter, QColor(153, 204, 255))
2884
                    model.setItem(24, columnCount, item)
2885

    
2886
                    item = set_item_properties(convert_to_fixed_point(hmb.velocity), Qt.AlignHCenter | Qt.AlignVCenter)
2887
                    item.setEditable(False)
2888
                    model.setItem(25, columnCount, item)
2889

    
2890
                    item = set_item_properties(convert_to_fixed_point(hmb.reynolds), Qt.AlignHCenter | Qt.AlignVCenter)
2891
                    item.setEditable(False)
2892
                    model.setItem(26, columnCount, item)
2893

    
2894
                    item = set_item_properties(convert_to_fixed_point(hmb.friction_factor), Qt.AlignHCenter | Qt.AlignVCenter)
2895
                    item.setEditable(False)
2896
                    model.setItem(27, columnCount, item)
2897

    
2898
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_drop), Qt.AlignHCenter | Qt.AlignVCenter)
2899
                    item.setEditable(False)
2900
                    model.setItem(28, columnCount, item)
2901

    
2902
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_drop_friction), Qt.AlignHCenter | Qt.AlignVCenter)
2903
                    item.setEditable(False)
2904
                    model.setItem(29, columnCount, item)
2905

    
2906
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_drop_static), Qt.AlignHCenter | Qt.AlignVCenter)
2907
                    item.setEditable(False)
2908
                    model.setItem(30, columnCount, item)
2909

    
2910
                    item = set_item_properties(convert_to_fixed_point(hmb.pressure_pipe_end_point), Qt.AlignHCenter | Qt.AlignVCenter)
2911
                    item.setEditable(False)
2912
                    model.setItem(31, columnCount, item)
2913

    
2914
                    item = set_item_properties(convert_to_fixed_point(hmb.power), Qt.AlignHCenter | Qt.AlignVCenter)
2915
                    item.setEditable(False)
2916
                    model.setItem(32, columnCount, item)
2917

    
2918
                    if hmb.isDeleted:
2919
                        self.tableWidgetHMB.hideColumn(columnCount)
2920

    
2921
                model.setHorizontalHeaderLabels(col_names)
2922
                self.tableWidgetHMB.setModel(model)
2923

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

    
2931
    '''
2932
        @brief      refresh scene
2933
        @author     humkyung
2934
        @date       2018.07.23
2935
    '''
2936

    
2937
    def onCommandRejected(self, cmd=None):
2938
        try:
2939
            if type(cmd) is PlaceStreamlineCommand.PlaceStreamlineCommand:
2940
                if self.actionLine.tag.streamline:
2941
                    self.graphicsView.scene.removeItem(self.actionLine.tag.streamline)
2942
                self.graphicsView.scene.update()
2943
                self.actionLine.tag.reset()
2944

    
2945
                self.actionLine.setChecked(False)
2946
            elif type(cmd) is AreaZoomCommand.AreaZoomCommand:
2947
                self.actionZoom.setChecked(False)
2948
            else:
2949
                if hasattr(self.actionLine, 'tag') and self.actionLine.tag.streamline:
2950
                    self.graphicsView.scene.removeItem(self.actionLine.tag.streamline)
2951
                    self.graphicsView.scene.update()
2952
                    self.actionLine.tag.reset()
2953

    
2954
                self.actionLine.setChecked(False)
2955
                self.actionZoom.setChecked(False)
2956
        finally:
2957
            self.graphicsView.useDefaultCommand()
2958

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

    
2976
            QMainWindow.keyPressEvent(self, event)
2977
        except Exception as ex:
2978
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2979
                      f'{sys.exc_info()[-1].tb_lineno}'
2980
            self.addMessage.emit(MessageType.Error, message)
2981

    
2982
    def on_item_removed(self, item):
2983
        """remove item from tree widget and then remove from scene"""
2984

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

    
2995
            matches = [_item for _item in self.graphicsView.scene.items() if hasattr(_item, 'owner')]
2996
            for match in matches:
2997
                if match.owner is item:
2998
                    match.owner = None
2999

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

    
3015
            if type(item) is QEngineeringStreamlineItem:
3016
                self.on_stream_line_deleted(item)
3017

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

    
3026
    '''
3027
        @brief      load components
3028
        @author     yeonjin
3029
        @date       2019.07.30.
3030
    '''
3031

    
3032
    def load_components(self, componentsUID):
3033
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
3034
        from EngineeringCalloutTextItem import QEngineeringCalloutTextItem
3035
        from EngineeringDimensionItem import QEngineeringDimensionItem
3036
        from EngineeringCloudItem import QEngineeringCloudItem
3037

    
3038
        try:
3039
            app_doc_data = AppDocData.instance()
3040

    
3041
            maxValue = len(componentsUID)
3042
            self.progress.setMaximum(maxValue)
3043

    
3044
            for componentUID in componentsUID:
3045
                componentInfos = app_doc_data.getComponentByComponentUID(componentUID)
3046
                if (len(componentInfos)) > 0:
3047
                    category = componentInfos[0]['Category']  # Category@SymbolType
3048
                    symbol_name = componentInfos[0]['Symbol_Name']  # Name@Symbols
3049

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

    
3078
                    self.progress.setValue(self.progress.value() + 1)
3079

    
3080
                QApplication.processEvents()
3081

    
3082
            #self.rebuild_label()
3083

    
3084
            # """ update scene """
3085
            self.graphicsView.scene.update(self.graphicsView.sceneRect())
3086
            for item in self.graphicsView.scene.items():
3087
                item.setVisible(True)
3088

    
3089
        except Exception as ex:
3090
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
3091
                                                           sys.exc_info()[-1].tb_lineno)
3092
            self.addMessage.emit(MessageType.Error, message)
3093
        finally:
3094
            pass
3095

    
3096
    '''
3097
        @brief      Remove added item on same place and Add GraphicsItem
3098
        @author     Jeongwoo
3099
        @date       2018.05.25
3100
        @history    2018.05.29  Jeongwoo    Moved from QRecognitionDialog
3101
                    2018.06.05  Jeongwoo    Remove Size condition
3102
                    2018.06.18  Jeongwoo    Set Z-index
3103
    '''
3104

    
3105
    def addTextItemToScene(self, textItem):
3106
        textItem.addTextItemToScene(self.graphicsView.scene)
3107

    
3108
    '''
3109
        @brief      Remove added item on same place and Add GraphicsItem
3110
        @author     Jeongwoo
3111
        @date       2018.05.29
3112
        @history    2018.06.18  Jeongwoo    Set Z-index
3113
    '''
3114

    
3115
    def addLineItemToScene(self, lineItem):
3116
        self.graphicsView.scene.addItem(lineItem)
3117

    
3118
    '''
3119
        @brief      Check Number
3120
        @author     kyouho
3121
        @date       2018.08.20
3122
    '''
3123

    
3124
    def isNumber(self, num):
3125
        p = re.compile('(^[0-9]+$)')
3126
        result = p.match(num)
3127

    
3128
        if result:
3129
            return True
3130
        else:
3131
            return False
3132

    
3133
    '''
3134
        @brief      find overlap Connector
3135
        @author     kyouho
3136
        @date       2018.08.28
3137
    '''
3138

    
3139
    def findOverlapConnector(self, connectorItem):
3140
        from shapely.geometry import Point
3141
        from EngineeringConnectorItem import QEngineeringConnectorItem
3142
        itemList = []
3143

    
3144
        x = connectorItem.center()[0]
3145
        y = connectorItem.center()[1]
3146

    
3147
        connectors = [item for item in self.graphicsView.scene.items() if
3148
                      type(item) is QEngineeringConnectorItem and item != connectorItem]
3149
        for connector in connectors:
3150
            if Point(x, y).distance(Point(connector.center()[0], connector.center()[1])) < 5:
3151
                itemList.append(connector.parent)
3152

    
3153
        return itemList
3154

    
3155
    def add_data(self, table_widget, name, value, unit, separation=None, is_cell_merge=None, foregroundcolor=None):
3156
        def make_table_widget_item(name: str, alignment, backgroundcolor=None, foregroundcolor=None) -> QTableWidgetItem:
3157
            if name is None:
3158
                name = ''
3159

    
3160
            item = QTableWidgetItem(str(name))
3161
            item.setTextAlignment(alignment)
3162
            if backgroundcolor:
3163
                item.setBackground(backgroundcolor)
3164
            if foregroundcolor:
3165
                item.setForeground(foregroundcolor)  # QBrush(QColor(255, 0, 0)))
3166
            return item
3167

    
3168
        row = table_widget.rowCount()
3169
        table_widget.setRowCount(row + 1)
3170

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

    
3196
        table_widget.resizeRowsToContents()
3197
        table_widget.resizeColumnsToContents()
3198

    
3199

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