프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / CodeTableDialog.py @ c16dd916

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

1
# coding: utf-8
2
"""
3
    This is code table dialog module
4
"""
5
import os
6
import sys, io
7
from PyQt5.QtCore import *
8
from PyQt5.QtGui import *
9
from PyQt5.QtWidgets import *
10
from AppDocData import AppDocData, MessageType
11
from App import App
12
from openpyxl import *
13
from openpyxl.styles import *
14

    
15
from PIL import Image
16
import tesseract_ocr_module as TOCR
17
import numpy as np
18

    
19
import CodeTable_UI
20
import FenceCommand
21
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
22

    
23

    
24
class QCodeTableDialog(QDialog):
25
    """ This code table dialog class """
26

    
27
    CODE_TABLES = (
28
    'Nominal Diameter', 'Fluid Code', 'Insulation Purpose', 'PnID Number', 'Piping Materials Class', 'Unit Number',
29
    'ValveOperCodes', 'EqpTagNames', 'ReservedWords', 'Dictionary')
30

    
31
    def __init__(self, parent, child_dialog=''):
32
        QDialog.__init__(self, parent)
33

    
34
        self.currentCode = {}
35

    
36
        self.code_area = None
37
        self.desc_area = None
38
        
39
        self.inst = False
40

    
41
        self.child_dialog = child_dialog
42
        if not child_dialog:
43
            self.ui = CodeTable_UI.Ui_CodeTableDialog()
44
            self.ui.setupUi(self)
45
            self.ui.buttonBox.button(QDialogButtonBox.Ok).setIcon(QIcon(':/newPrefix/OK.svg'))
46
            self.ui.buttonBox.button(QDialogButtonBox.Cancel).setIcon(QIcon(':/newPrefix/Remove.svg'))
47

    
48
            # unused : valve oper code, eq tag, reserved table
49
            self.ui.tabWidget.removeTab(8)
50
            self.ui.tabWidget.removeTab(7)
51
            self.ui.tabWidget.removeTab(6)
52

    
53
            self.ui.spinBoxHeight.setValue(50)
54

    
55
            self.ui.tableWidgetNominalDiameter.setSortingEnabled(True)
56
            self.ui.tableWidgetFluidCode.setSortingEnabled(True)
57
            self.ui.tableWidgetInsulationPurpose.setSortingEnabled(True)
58
            self.ui.tableWidgetPnIDNumber.setSortingEnabled(True)
59
            self.ui.tableWidgetPipingMaterialsClass.setSortingEnabled(True)
60
            self.ui.tableWidgetUnitNumber.setSortingEnabled(True)
61
            self.ui.tableWidgetValveOperCodes.setSortingEnabled(True)
62
            self.ui.tableWidgetEqpTagNames.setSortingEnabled(True)
63
            self.ui.tableWidgetReservedWords.setSortingEnabled(True)
64
            self.ui.tableWidgetDictionary.setSortingEnabled(True)
65

    
66
            # DB Table명 기준으로 작성
67
            for table in QCodeTableDialog.CODE_TABLES:
68
                self.settingTable(table)
69

    
70
            # connect signals
71
            self.ui.pushButtonImport.clicked.connect(self.import_code_table)
72
            self.ui.pushButtonExport.clicked.connect(self.export_code_table)
73
            self.ui.pushButtonRead.clicked.connect(self.read_from_legend)
74

    
75
        self.graphicsView = App.mainWnd().graphicsView
76
        self.fence_cmd = FenceCommand.FenceCommand(self.graphicsView)
77
        self.fence_cmd.onSuccess.connect(self.onAreaCreated)
78

    
79
    def getTabText(self):
80
        """ get current tab name text """
81
        if not self.child_dialog:
82
            _tabWidget = self.ui.tabWidget
83
            currentTabIndex = _tabWidget.currentIndex()
84
            tabText = self.replaceText(_tabWidget.tabText(currentTabIndex))
85
        else:
86
            tabText = self.child_dialog
87
        return tabText
88

    
89
    def read_from_legend(self):
90
        """ read code data from legend drawing """
91
        if self.ui.pushButtonRead.text() == 'Read from Legend':
92
            self.graphicsView.command = self.fence_cmd
93
            self.ui.pushButtonRead.setText('Draw Code Area')
94
        elif self.ui.pushButtonRead.text() == 'Read' and self.code_area and self.code_area.scene() and self.desc_area and self.desc_area.scene():
95
            # read ocr
96
            app_doc_data = AppDocData.instance()
97
            code_rect = self.code_area.sceneBoundingRect()
98
            desc_rect = self.desc_area.sceneBoundingRect()
99
            #code_img = self.graphicsView.image().copy(code_rect.x(), code_rect.y(), code_rect.width(), code_rect.height())
100
            #desc_img = self.graphicsView.image().copy(desc_rect.x(), desc_rect.y(), desc_rect.width(), desc_rect.height())
101
            code_img = app_doc_data.activeDrawing.image.copy()
102
            code_img = code_img[int(code_rect.y()):int(code_rect.y() + code_rect.height()), \
103
                    int(code_rect.x()):int(code_rect.x() + code_rect.width())]
104
            desc_img = app_doc_data.activeDrawing.image.copy()
105
            desc_img = desc_img[int(desc_rect.y()):int(desc_rect.y() + desc_rect.height()), \
106
                    int(desc_rect.x()):int(desc_rect.x() + desc_rect.width())]
107
            code_texts = self.detectText(code_img, code_rect)
108
            desc_texts = self.detectText(desc_img, desc_rect)
109

    
110
            # check validate
111
            for code_text in code_texts:
112
                code_text.desc = []
113

    
114
            if desc_texts:
115
                for desc_index in reversed(range(len(desc_texts))):
116
                    for code_index in range(len(code_texts)):
117
                        if abs(desc_texts[desc_index].center[1] - code_texts[code_index].center[1]) < round(self.ui.spinBoxHeight.value() / 2):
118
                            code_texts[code_index].desc.append(desc_texts[desc_index])
119
                            desc_texts.pop(desc_index)
120
                            break
121

    
122
                for desc_index in reversed(range(len(desc_texts))):
123
                    min_distance = sys.maxsize
124
                    min_code = None
125
                    for code_index in range(len(code_texts)):
126
                        distance = desc_texts[desc_index].center[1] - code_texts[code_index].center[1]
127
                        if distance > 0 and distance < min_distance:
128
                            min_distance = distance
129
                            min_code = code_texts[code_index]
130

    
131
                    if min_code:
132
                        min_code.desc.append(desc_texts[desc_index])
133
                        desc_texts.pop(desc_index)
134
                    
135

    
136
                #QMessageBox.warning(self, self.tr('Notice'), self.tr('Please check text area.'))
137
                #return  
138

    
139
            desc_texts = []
140
            for code_text in code_texts:
141
                desc = ' '.join([desc.getText() for desc in sorted(code_text.desc, key=lambda desc: desc.center[1])])
142
                desc_texts.append(desc)
143

    
144
            # fill table
145
            tabText = self.getTabText()
146

    
147
            if tabText == 'NominalDiameter':
148
                return
149
            table = self.findTableWidget(tabText)
150

    
151
            table.cellChanged.disconnect(self.cellValueChanged)
152
            
153
            past_count = table.rowCount()
154
            for row_index in range(past_count):
155
                for code_index in reversed(range(len(code_texts))):
156
                    if table.isRowHidden(row_index): continue
157
                    if table.item(row_index, 1) and table.item(row_index, 1).text() == code_texts[code_index].getText():
158
                        table.setItem(row_index, 2, QTableWidgetItem(desc_texts[code_index]))
159
                        code_texts.pop(code_index)
160
                        desc_texts.pop(code_index)
161
                        break
162
            table.setRowCount(past_count + len(code_texts))
163
            for code_index in range(len(code_texts)):
164
                table.setItem(past_count + code_index - 1, 0, QTableWidgetItem(''))
165
                table.setItem(past_count + code_index - 1, 1, QTableWidgetItem(code_texts[code_index].getText()))
166
                table.setItem(past_count + code_index - 1, 2, QTableWidgetItem(desc_texts[code_index]))
167
                if self.ui.checkBoxAllowable.isChecked():
168
                    table.setItem(past_count + code_index - 1, 3, QTableWidgetItem(self.makeAllowable(code_texts[code_index].getText())))
169

    
170
            last_empty_row = table.rowCount()
171
            table.setItem(last_empty_row - 1, 0, QTableWidgetItem(''))
172
            table.setItem(last_empty_row - 1, 1, QTableWidgetItem(''))
173
            table.setItem(last_empty_row - 1, 2, QTableWidgetItem(''))
174

    
175
            table.cellChanged.connect(self.cellValueChanged)
176

    
177
            if self.code_area:
178
                if self.code_area.scene():
179
                    self.graphicsView.scene().removeItem(self.code_area)
180
                self.code_area = None
181
            if self.desc_area:
182
                if self.desc_area.scene():
183
                    self.graphicsView.scene().removeItem(self.desc_area)
184
                self.desc_area = None
185
            self.ui.pushButtonRead.setText('Read from Legend')
186
        else:
187
            if self.code_area:
188
                if self.code_area.scene():
189
                    self.graphicsView.scene().removeItem(self.code_area)
190
                self.code_area = None
191
            if self.desc_area:
192
                if self.desc_area.scene():
193
                    self.graphicsView.scene().removeItem(self.desc_area)
194
                self.desc_area = None
195
            self.ui.pushButtonRead.setText('Read from Legend')
196

    
197
    def onAreaCreated(self, x, y , width, height):
198
        import uuid
199
        THICKNESS = 5
200

    
201
        if self.ui.pushButtonRead.text() == 'Draw Code Area':
202
            item = QGraphicsBoundingBoxItem(x, y, width, height)
203
            item.setPen(QPen(Qt.red, THICKNESS, Qt.SolidLine))
204
            self.graphicsView.scene().addItem(item)
205
            self.code_area = item
206
            self.ui.pushButtonRead.setText('Draw Description Area')
207
        elif self.ui.pushButtonRead.text() == 'Draw Description Area' and self.code_area and self.code_area.scene():
208
            item = QGraphicsBoundingBoxItem(x, y, width, height)
209
            item.setPen(QPen(Qt.blue, THICKNESS, Qt.SolidLine))
210
            self.graphicsView.scene().addItem(item)
211
            self.desc_area = item
212
            self.ui.pushButtonRead.setText('Read')
213
            self.graphicsView.command = None
214

    
215
    def detectText(self, image, rect):
216
        """ detect text from image, come from OcrResultDialog and modified """
217
        try:
218
            '''
219
            buffer = QBuffer()
220
            buffer.open(QBuffer.ReadWrite)
221
            image.save(buffer, "PNG")
222
            pyImage = Image.open(io.BytesIO(buffer.data()))
223
            img = np.array(pyImage)
224
            '''
225
            
226
            docData = AppDocData.instance()
227
            configs = docData.getConfigs('Text Recognition', 'OCR Data')
228
            ocr_data = configs[0].value if 1 == len(configs) else 'eng'
229

    
230
            whiteCharList = docData.getConfigs('Text Recognition', 'White Character List')
231
            if len(whiteCharList) is 0:
232
                textInfoList = TOCR.getTextInfo(image, (round(rect.x()), round(rect.y())), 0, language=ocr_data)
233
            else:
234
                textInfoList = TOCR.getTextInfo(image, (round(rect.x()), round(rect.y())), 0, language=ocr_data, conf = whiteCharList[0].value)
235

    
236
            if textInfoList is not None and len(textInfoList) > 0:
237
                return textInfoList
238
        except Exception as ex:
239
            from App import App 
240
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
241
            App.mainWnd().addMessage.emit(MessageType.Error, message)
242

    
243
    '''
244
        @brief      Setting Table
245
        @author     kyouho
246
        @date       2018.07.10
247
    '''
248

    
249
    def settingTable(self, tableName, attribute_uid=None, tableDatas=None):
250
        try:
251
            tableName = self.replaceText(tableName)
252
            docData = AppDocData.instance()
253
            table = self.findTableWidget(tableName)
254
            if table: table.horizontalHeader().setStretchLastSection(True)
255
            if tableName == "NominalDiameter":
256
                tableDatas = docData.getNomialPipeSizeData()
257
            elif tableName == "SymbolAttributeCodeTable":
258
                if tableDatas is None:
259
                    tableDatas = docData.getCodeTable(tableName, forCheckLineNumber=False, symbol_attribute_uid=attribute_uid)
260
                else:
261
                    pass
262
            elif tableName == 'CustomCodes':
263
                if tableDatas is None:
264
                    tableDatas = docData.getCodeTable(tableName, forCheckLineNumber=False, custom_table_uid=attribute_uid)
265
                else:
266
                    pass
267
            elif tableName == 'InstCodes':
268
                if tableDatas is None:
269
                    tableDatas = docData.getCodeTable(tableName, forCheckLineNumber=False, inst_table_uid=attribute_uid)
270
                else:
271
                    pass
272
            else:
273
                tableDatas = docData.getCodeTable(tableName)
274

    
275
            if tableName == "NominalDiameter":
276
                self.fill_nominal_pipe_sizes(tableDatas)
277

    
278
                table.cellChanged.connect(self.cellValueChanged)
279
                self.checkRowAndAddRow(tableName, table)
280
            else:
281
                if not self.inst:
282
                    table.setColumnCount(4)
283
                    table.setHorizontalHeaderLabels(['UID', 'Code', 'Desc.', 'Allowables'])
284
                else:
285
                    table.setColumnCount(7)
286
                    table.setHorizontalHeaderLabels(['UID', 'Code', 'Symbols', 'Attribute', 'New Code', 'Expression', 'Priority'])
287
                table.hideColumn(0)
288

    
289
                self.fill_codes(table, tableDatas)
290

    
291
                #table.horizontalHeaderItem(1).setSizeHint(QSize(30, 30))
292
                table.cellChanged.connect(self.cellValueChanged)
293
                self.checkRowAndAddRow(tableName, table)
294
                self.setCurrentCode(table, tableName)
295
        except Exception as ex:
296
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
297
                                                       sys.exc_info()[-1].tb_lineno))
298

    
299
    '''
300
        @brief      Insert row NominalPipeSzie Tablewidget
301
        @author     kyouho
302
        @date       2018.07.10
303
    '''
304

    
305
    def fill_nominal_pipe_sizes(self, pipeSizes):
306
        try:
307
            self.ui.tableWidgetNominalDiameter.setColumnCount(8)
308
            self.ui.tableWidgetNominalDiameter.setHorizontalHeaderLabels(
309
                ['UID', 'Code', 'Metric', 'Inch', 'InchStr', 'Inch Allowables', 'MetricStr', 'Metric Allowables'])
310
            self.ui.tableWidgetNominalDiameter.hideColumn(0)
311
            self.ui.tableWidgetNominalDiameter.setRowCount(len(pipeSizes))
312
            row = 0
313
            for pipeSize in pipeSizes:
314
                item = QTableWidgetItem(pipeSize.uid)
315
                item.setData(Qt.UserRole, pipeSize)
316
                self.ui.tableWidgetNominalDiameter.setItem(row, 0, item)
317
                self.ui.tableWidgetNominalDiameter.setItem(row, 1, QTableWidgetItem(pipeSize.code))
318
                self.ui.tableWidgetNominalDiameter.setItem(row, 2, QTableWidgetItem(
319
                    '' if pipeSize.metric is None else str(pipeSize.metric)))
320
                self.ui.tableWidgetNominalDiameter.setItem(row, 3, QTableWidgetItem(
321
                    '' if pipeSize.inch is None else str(pipeSize.inch)))
322
                self.ui.tableWidgetNominalDiameter.setItem(row, 4, QTableWidgetItem(
323
                    '' if pipeSize.inchStr is None else pipeSize.inchStr))
324
                self.ui.tableWidgetNominalDiameter.setItem(row, 5, QTableWidgetItem(
325
                    '' if pipeSize.allowable_inch_str is None else pipeSize.allowable_inch_str))
326
                self.ui.tableWidgetNominalDiameter.setItem(row, 6, QTableWidgetItem(
327
                    '' if pipeSize.metricStr is None else pipeSize.metricStr))
328
                self.ui.tableWidgetNominalDiameter.setItem(row, 7, QTableWidgetItem(
329
                    '' if pipeSize.allowable_metric_str is None else pipeSize.allowable_metric_str))
330
                row += 1
331

    
332
            self.ui.tableWidgetNominalDiameter.horizontalHeaderItem(0).setSizeHint(QSize(30, 30))
333
        except Exception as ex:
334
            from App import App
335

    
336
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
337
                                                           sys.exc_info()[-1].tb_lineno)
338
            App.mainWnd().addMessage.emit(MessageType.Error, message)
339

    
340
    '''
341
        @brief      Insert row Common Tablewidget
342
        @author     kyouho
343
        @date       2018.07.10
344
    '''
345

    
346
    def fill_codes(self, table, tableDatas, from_exel=False):
347
        try:
348
            table.setRowCount(len(tableDatas))
349
            row = 0
350
            for tableData in tableDatas:
351
                if not self.inst:
352
                    table.setItem(row, 0, QTableWidgetItem(tableData[0]))  # UID
353
                    table.setItem(row, 1, QTableWidgetItem(tableData[1]))  # Code
354
                    table.setItem(row, 2, QTableWidgetItem(tableData[2] if tableData[2] else ''))  # Description
355
                    if not from_exel:
356
                        table.setItem(row, 3, QTableWidgetItem((tableData[3] if type(tableData[3]) is str else ','.join(tableData[3])) \
357
                                                                                    if tableData[3] else ''))  # Allowables
358
                    else:
359
                        table.setItem(row, 3, QTableWidgetItem(self.makeAllowable(tableData[1]) if tableData[1] else ''))
360
                else:
361
                    table.setItem(row, 0, QTableWidgetItem(tableData[0]))  # UID
362
                    table.setItem(row, 1, QTableWidgetItem(','.join(tableData[1])))  # Code
363
                    table.setItem(row, 2, QTableWidgetItem(','.join(tableData[2])))  # Symbols
364
                    table.setItem(row, 3, QTableWidgetItem(','.join(tableData[3])))  # Attribute
365
                    table.setItem(row, 4, QTableWidgetItem(tableData[4]))  # New Code
366
                    table.setItem(row, 5, QTableWidgetItem(tableData[5]))  # Expression
367
                    table.setItem(row, 6, QTableWidgetItem(tableData[6]))  # Priority
368

    
369
                row += 1
370
        except Exception as ex:
371
            from App import App
372

    
373
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
374
                                                           sys.exc_info()[-1].tb_lineno)
375
            App.mainWnd().addMessage.emit(MessageType.Error, message)
376

    
377
    '''
378
        @brief      Find TableWidget with Name
379
        @author     kyouhokyouho
380
        @date       2018.07.10
381
    '''
382

    
383
    def findTableWidget(self, tableName):
384
        tableName = self.replaceText(tableName)
385
        return self.findChild(QTableWidget, 'tableWidget' + tableName)
386

    
387
    '''
388
        @brief      key press event
389
        @author     kyouho
390
        @date       2018.07.10
391
    '''
392

    
393
    def keyPressEvent(self, e):
394
        try:
395
            if e.key() == Qt.Key_Delete:
396
                tabText = self.getTabText()
397
                
398
                table = self.findTableWidget(tabText)
399
                if table:
400
                    selectedIndexes = table.selectedIndexes()
401
                    selectedRows = [item.row() for item in selectedIndexes]
402
                    model = table.model()
403

    
404
                    rowsIndex = []
405
                    for row in selectedRows:
406
                        rowsIndex.append(row)
407

    
408
                    # 중복 제거
409
                    rowsIndex = list(set(rowsIndex))
410
                    rowsIndex.reverse()
411

    
412
                    if tabText != "NominalDiameter":
413
                        for row in rowsIndex:
414
                            table.hideRow(row)
415
                            """
416
                            uid = table.item(row, 0).text()
417
                            self.removeUID[uid] = tabText
418
                            model.removeRow(row)
419
                            """
420

    
421
                        self.checkRowAndAddRow(tabText, table)
422
            elif (e.key() == Qt.Key_C) and (e.modifiers() & Qt.ControlModifier):
423
                tabText = self.getTabText()
424
                
425
                table = self.findTableWidget(tabText)
426
                if table:
427
                    self.copy_selection(table)
428
            elif (e.key() == Qt.Key_V) and (e.modifiers() & Qt.ControlModifier):
429
                tabText = self.getTabText()
430
                '''
431
                table = self.findTableWidget(tabText)
432
                tab = self.ui.tabWidget.widget(self.ui.tabWidget.currentIndex())
433
                table = tab.findChild(QTableWidget)
434
                '''
435
                table = self.findTableWidget(tabText)
436
                if table:
437
                    self.paste_selection(table)
438
        except Exception as ex:
439
            from App import App
440
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
441
                                                           sys.exc_info()[-1].tb_lineno)
442
            App.mainWnd().addMessage.emit(MessageType.Error, message)
443

    
444
    def copy_selection(self, table_widget):
445
        """copy selected text to clipboard"""
446

    
447
        import io
448
        import csv
449

    
450
        selection = table_widget.selectedIndexes()
451
        if selection:
452
            rows = sorted(index.row() for index in selection)
453
            columns = sorted(index.column() for index in selection)
454
            rowcount = rows[-1] - rows[0] + 1
455
            colcount = columns[-1] - columns[0] + 1
456
            table = [[''] * colcount for _ in range(rowcount)]
457
            for index in selection:
458
                row = index.row() - rows[0]
459
                column = index.column() - columns[0]
460
                table[row][column] = index.data()
461
            stream = io.StringIO()
462
            csv.writer(stream, delimiter='\t').writerows(table)
463
            QApplication.clipboard().setText(stream.getvalue())
464

    
465
    def paste_selection(self, table_widget):
466
        """paste text of clipboard to table widget"""
467

    
468
        import io
469
        import csv
470

    
471
        selection = table_widget.selectedIndexes()
472
        if selection:
473
            model = table_widget.model()
474

    
475
            buffer = QApplication.clipboard().text()
476
            rows = sorted(index.row() for index in selection)
477
            columns = sorted(index.column() for index in selection)
478
            reader = csv.reader(io.StringIO(buffer), delimiter='\t')
479
            if len(rows) == 1 and len(columns) == 1:
480
                for i, line in enumerate(reader):
481
                    for j, cell in enumerate(line):
482
                        model.setData(model.index(rows[0] + i, columns[0] + j), cell)
483
            else:
484
                arr = [[cell for cell in row] for row in reader]
485
                for index in selection:
486
                    row = index.row() - rows[0]
487
                    column = index.column() - columns[0]
488
                    model.setData(model.index(index.row(), index.column()), arr[row][column])
489

    
490
    '''
491
        @brief      Add new row
492
        @author     kyouho
493
        @date       2018.07.10
494
    '''
495

    
496
    def checkRowAndAddRow(self, tableName, table):
497
        try:
498
            rowCount = table.rowCount()
499
            result = True
500
            if tableName != "NominalDiameter":
501
                for row in range(rowCount):
502
                    if table.isRowHidden(row): continue
503
                    if not self.inst:
504
                        code = table.item(row, 1).text()
505
                    else:
506
                        texts = [[table.item(row, 1).text(), table.item(row, 2).text(), table.item(row, 3).text()], \
507
                                    [table.item(row, 4).text(), table.item(row, 5).text()]]
508
                        code = True if len([text for text in texts[0] if text == '']) <= 2 and len([text for text in texts[1] if text == '']) <= 1 else False
509
                    if not code:
510
                        result = False
511
                if result:
512
                    table.cellChanged.disconnect(self.cellValueChanged)
513
                    table.setRowCount(rowCount + 1)
514
                    table.setItem(rowCount, 0, QTableWidgetItem(''))
515
                    table.setItem(rowCount, 1, QTableWidgetItem(''))
516
                    table.setItem(rowCount, 2, QTableWidgetItem(''))
517
                    table.setItem(rowCount, 3, QTableWidgetItem(''))
518
                    if self.inst:
519
                        table.setItem(rowCount, 4, QTableWidgetItem(''))
520
                        table.setItem(rowCount, 5, QTableWidgetItem(''))
521
                        table.setItem(rowCount, 6, QTableWidgetItem(''))
522
                    table.cellChanged.connect(self.cellValueChanged)
523
            else:
524
                columnCount = table.columnCount()
525

    
526
                for row in range(rowCount):
527
                    if not result:
528
                        break
529
                    for columnIndex in range(columnCount):
530
                        if not table.item(row, columnIndex).text():
531
                            result = False
532
                            break
533

    
534
                if result:
535
                    table.setRowCount(rowCount + 1)
536
                    table.cellChanged.disconnect(self.cellValueChanged)
537
                    for columnIndex in range(columnCount):
538
                        table.setItem(rowCount, columnIndex, QTableWidgetItem(''))
539
                    table.cellChanged.connect(self.cellValueChanged)
540

    
541
        except Exception as ex:
542
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
543
                                                       sys.exc_info()[-1].tb_lineno))
544

    
545
    def makeAllowable(self, code):
546
        ''' make allowable for code '''
547
        import re
548

    
549
        founds = []
550
        code_ori = code
551
        app_doc_data = AppDocData.instance()
552
        configs = app_doc_data.getConfigs('Text Recognition', 'Allowable Pair')
553

    
554
        pair = configs[0].value if 1 == len(configs) else "1Il, 0OD, 8B, 2Z, Ss5"
555
        pair = pair.replace(' ', '').split(',')
556
        allowable_list = [list(text) for text in pair]
557
        for allowables in allowable_list:                
558
            f = '(' + '|'.join(allowables) + ')'
559
            for chars in re.finditer(f, code):
560
                founds.append((chars.start(), chars.end(), f))
561

    
562
        founds = sorted(founds, key=lambda param:param[0], reverse=True)
563
        for found in founds:
564
            code = code[:found[0]] + found[2] + code[found[0] + 1:]
565

    
566
        if code_ori != code:
567
            return code
568
        else:
569
            return ''
570

    
571
    '''
572
        @brief      cellValueChange event
573
        @author     kyouho
574
        @date       2018.07.10
575
    '''
576
    def cellValueChanged(self, row, column):
577
        try:
578
            tabText = self.getTabText()
579

    
580
            table = self.findTableWidget(tabText)
581

    
582
            if tabText != "NominalDiameter" and not self.inst:
583
                item = table.item(row, 1)
584
                if not item: return
585
                code = item.text()
586
                if code == '': return
587

    
588
                if column == 1:
589
                    result = self.isExistCode(table, code)
590
                    if result:
591
                        self.checkRowAndAddRow(tabText, table)
592
                        self.setCurrentCode(table, tabText)
593

    
594
                        # make allowable
595
                        if self.ui.checkBoxAllowable.isChecked():
596
                            table.setItem(row, 3, QTableWidgetItem(self.makeAllowable(code)))                            
597
                    else:
598
                        QMessageBox.warning(self, self.tr('Notice'),
599
                                            self.tr('The same code already exists in the table.'))
600
                        table.cellChanged.disconnect(self.cellValueChanged)
601
                        item.setText(self.currentCode[tabText][row])
602
                        table.cellChanged.connect(self.cellValueChanged)
603
                elif column == 2:
604
                    table.resizeColumnToContents(2)
605
                else:
606
                    table.resizeColumnToContents(3)
607
            elif self.inst:
608
                self.checkRowAndAddRow(tabText, table)
609
                self.setCurrentCode(table, tabText)
610
            else:
611
                self.checkRowAndAddRow(tabText, table)
612
        except Exception as ex:
613
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
614
                                                       sys.exc_info()[-1].tb_lineno))
615

    
616
    '''
617
        @brief      Check Duplicate Code 
618
        @author     kyouho
619
        @date       2018.07.10
620
    '''
621

    
622
    def isExistCode(self, table, editCode):
623
        try:
624
            if not editCode:
625
                return False
626

    
627
            rowCount = table.rowCount()
628
            codes = []
629
            for row in range(rowCount):
630
                if table.isRowHidden(row): continue
631
                code = table.item(row, 1).text()
632
                codes.append(code)
633

    
634
            count = codes.count(editCode)
635

    
636
            if count >= 2:
637
                return False
638
            else:
639
                return True
640

    
641
        except Exception as ex:
642
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
643
                                                       sys.exc_info()[-1].tb_lineno))
644

    
645
    '''
646
        @brief      save current Code (self.currentCode)
647
        @author     kyouho
648
        @date       2018.07.10
649
    '''
650

    
651
    def setCurrentCode(self, table, tabText):
652
        try:
653
            self.currentCode[tabText] = {}
654
            rowCount = table.rowCount()
655

    
656
            res = {}
657
            for row in range(rowCount):
658
                code = table.item(row, 1).text()
659
                res[row] = code
660

    
661
            self.currentCode[tabText] = res
662

    
663
        except Exception as ex:
664
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
665
                                                       sys.exc_info()[-1].tb_lineno))
666

    
667
    '''
668
        @brief      replaceTextForCodeTable
669
        @author     kyouho
670
        @date       2018.07.12
671
    '''
672

    
673
    def replaceText(self, text):
674
        return text.replace(' ', '').replace('&&', 'n')
675

    
676
    def import_code_table(self):
677
        """import code table excel file"""
678

    
679
        options = QFileDialog.Options()
680
        options |= QFileDialog.DontUseNativeDialog
681
        file_name, _ = QFileDialog.getOpenFileName(self, "Import code table", os.getcwd(), "xlsx files(*.xlsx)",
682
                                                   options=options)
683
        if file_name:
684
            QApplication.setOverrideCursor(Qt.WaitCursor)
685
            try:
686
                app_doc_data = AppDocData.instance()
687
                book = load_workbook(file_name)
688
                for sheet in book.worksheets:
689
                    matches = [index for index in range(self.ui.tabWidget.count())
690
                               if sheet.title == self.ui.tabWidget.tabText(index)]
691
                    if matches:
692
                        table = self.ui.tabWidget.widget(matches[0]).findChild(QTableWidget)
693
                        self.fill_table_with_sheet(sheet, table)
694
            except Exception as ex:
695
                from App import App
696
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
697
                                                               sys.exc_info()[-1].tb_lineno)
698
                App.mainWnd().addMessage.emit(MessageType.Error, message)
699
            finally:
700
                QApplication.restoreOverrideCursor()
701

    
702

    
703
    def export_code_table(self):
704
        """export code table to excel file"""
705

    
706
        app_doc_data = AppDocData.instance()
707
        project = app_doc_data.getCurrentProject()
708

    
709
        options = QFileDialog.Options()
710
        options |= QFileDialog.DontUseNativeDialog
711
        file_name, _ = QFileDialog.getSaveFileName(self, "Export Code Table", project.path, "xlsx files(*.xlsx)",
712
                                                   options=options)
713
        if not file_name:
714
            return
715

    
716
        QApplication.setOverrideCursor(Qt.WaitCursor)
717

    
718
        try:
719
            wb = Workbook()
720
            wb.active.title = self.tr(self.ui.tabWidget.tabText(0))
721
            for index in range(1, self.ui.tabWidget.count()):
722
                wb.create_sheet(self.tr(self.ui.tabWidget.tabText(index)))
723

    
724
            for index in range(self.ui.tabWidget.count()):
725
                tab = self.ui.tabWidget.widget(index)
726
                table = tab.findChild(QTableWidget)
727
                if table:
728
                    sheet = wb.worksheets[index]
729
                    self.set_sheet_header(table, sheet)
730
                    self.fill_sheet_with_table(table, sheet)
731
                    self.auto_resize_columns(sheet)
732

    
733
            file_name, ext = os.path.splitext(file_name)
734
            save_file_name = file_name + ext if ext.upper() == '.XLSX' else file_name + '.xlsx'
735
            wb.save(save_file_name)
736

    
737
            QMessageBox.about(self, self.tr("Information"), self.tr('Successfully saved.'))
738

    
739
            os.startfile(save_file_name)
740
        except Exception as ex:
741
            from App import App
742
            from AppDocData import MessageType
743

    
744
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
745
                                                           sys.exc_info()[-1].tb_lineno)
746
            App.mainWnd().addMessage.emit(MessageType.Error, message)
747
        finally:
748
            QApplication.restoreOverrideCursor()
749

    
750
    def set_sheet_header(self, table, sheet):
751
        """ set list header """
752

    
753
        try:
754
            thin = Side(border_style='thin', color='000000')
755
            border = Border(left=thin, right=thin, top=thin, bottom=thin)
756
            _col = 1
757
            for col in range(table.columnCount()):
758
                logical_index = table.horizontalHeader().logicalIndex(col)
759
                col_name = table.horizontalHeaderItem(logical_index).text()
760
                sheet.cell(1, _col, col_name)
761
                sheet.cell(row=1, column=_col).alignment = Alignment(horizontal='center', vertical='center',
762
                                                                     wrapText=True)
763
                sheet.cell(row=1, column=_col).fill = PatternFill(patternType='solid', fill_type='solid',
764
                                                                  fgColor=Color('8DB4E2'))
765
                sheet.cell(row=1, column=_col).border = border
766
                _col += 1
767
        except Exception as ex:
768
            from App import App
769
            from AppDocData import MessageType
770

    
771
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
772
                                                           sys.exc_info()[-1].tb_lineno)
773
            App.mainWnd().addMessage.emit(MessageType.Error, message)
774

    
775
    def fill_table_with_sheet(self, sheet, table):
776
        """fill table with sheet"""
777
        from NominalPipeSize import NominalPipeSize
778

    
779
        try:
780
            row_index = 0
781
            headers = {}
782
            if sheet.title == 'Nominal Diameter':
783
                pipe_sizes = []
784
                for row in sheet.rows:
785
                    if row_index == 0:
786
                        for col in range(sheet.max_column):
787
                            value = row[col].value
788
                            headers[col] = value
789
                    else:
790
                        pipe_size = NominalPipeSize(None, None, None, None, None, None, None, None)
791
                        for col in range(sheet.max_column):
792
                            value = row[col].value
793
                            if 'UID' == headers[col]:
794
                                pipe_size.uid = value
795
                            elif 'Code' == headers[col]:
796
                                pipe_size.code = value
797
                            elif 'Metric' == headers[col]:
798
                                pipe_size.metric = value
799
                            elif 'Inch' == headers[col]:
800
                                pipe_size.inch = value
801
                            elif 'InchStr' == headers[col]:
802
                                pipe_size.inchStr = value
803
                            elif 'Inch Allowables' == headers[col]:
804
                                pipe_size.allowable_inch_str = value
805
                            elif 'MetricStr' == headers[col]:
806
                                pipe_size.metricStr = value
807
                            elif 'Metric Allowables' == headers[col]:
808
                                pipe_size.allowable_metric_str = value
809

    
810
                        pipe_sizes.append(pipe_size)
811

    
812
                    row_index += 1
813

    
814
                self.fill_nominal_pipe_sizes(pipe_sizes)
815
            else:
816
                codes = []
817
                for row in sheet.rows:
818
                    if row_index == 0:
819
                        for col in range(sheet.max_column):
820
                            value = row[col].value
821
                            headers[col] = value
822
                    else:
823
                        code = [row[col].value for col in range(sheet.max_column)]
824
                        codes.append(code)
825

    
826
                    row_index += 1
827

    
828
                from_exel = True if self.ui.checkBoxAllowable.isChecked() else False
829
                self.fill_codes(table, codes, from_exel=from_exel)
830
        except Exception as ex:
831
            from App import App
832
            from AppDocData import MessageType
833

    
834
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
835
                                                           sys.exc_info()[-1].tb_lineno)
836
            App.mainWnd().addMessage.emit(MessageType.Error, message)
837

    
838
    def fill_sheet_with_table(self, table, sheet):
839
        """fill sheet with table"""
840

    
841
        try:
842
            thin = Side(border_style='thin', color='000000')
843
            border = Border(left=thin, right=thin, top=thin, bottom=thin)
844

    
845
            for col in range(table.columnCount()):
846
                for row in range(table.rowCount()):
847
                    try:
848
                        text = str(table.item(row, col).text())
849
                        sheet.cell(row + 2, col + 1, text)
850
                        sheet.cell(row=row + 2, column=col + 1).border = border
851
                    except AttributeError:
852
                        pass
853
        except Exception as ex:
854
            from App import App
855
            from AppDocData import MessageType
856

    
857
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
858
                                                           sys.exc_info()[-1].tb_lineno)
859
            App.mainWnd().addMessage.emit(MessageType.Error, message)
860

    
861
    def auto_resize_columns(self, sheet):
862
        """ auto resize columns with contents """
863

    
864
        from openpyxl.utils import get_column_letter
865
        try:
866
            for col in sheet.columns:
867
                max_length = 0
868
                column = col[0].column  # Get the column name
869
                for cell in col:
870
                    try:  # Necessary to avoid error on empty cells
871
                        if len(str(cell.value)) > max_length:
872
                            max_length = len(cell.value)
873
                    except:
874
                        pass
875

    
876
                adjusted_width = (max_length + 2) * 1.2
877
                sheet.column_dimensions[
878
                    get_column_letter(column) if type(column) is int else column].width = adjusted_width
879
        except Exception as ex:
880
            from App import App
881
            from AppDocData import MessageType
882

    
883
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
884
                                                           sys.exc_info()[-1].tb_lineno)
885
            App.mainWnd().addMessage.emit(MessageType.Error, message)
886

    
887
    '''
888
        @brief      save codes
889
        @author     kyouho
890
        @date       2018.07.12
891
    '''
892

    
893
    def accept(self):
894
        from CodeTables import CodeTable
895
        
896
        for table in QCodeTableDialog.CODE_TABLES:
897
            if table != 'Nominal Diameter':
898
                self.saveCommonCodeData(table)
899

    
900
        self.saveNomialPipeSize()
901
        CodeTable.clearTables()
902

    
903
        QDialog.accept(self)
904

    
905
    '''
906
        @brief      save common code data
907
        @author     kyouho
908
        @date       2018.07.12
909
    '''
910
    def saveCommonCodeData(self, tableName):
911
        datas = []
912
        try:
913
            tableName = self.replaceText(tableName)
914
            table = self.findTableWidget(tableName)
915
            rowCount = table.rowCount()
916
            for row in range(rowCount):
917
                if table.isRowHidden(row):
918
                    continue
919
                    #uid, code, description, allowables = '-1', table.item(row, 1).text(), '', table.item(row, 0).text()
920
                elif table.item(row, 0):
921
                    uid = table.item(row, 0).text()
922
                    code = table.item(row, 1).text()
923
                    description = table.item(row, 2).text() if table.item(row, 2) is not None else ''
924
                    allowables = table.item(row, 3).text() if table.item(row, 3) is not None else ''
925

    
926
                if code:
927
                    datas.append((uid, code, description, allowables))
928

    
929
            docData = AppDocData.instance()
930
            docData.saveCommonCodeData(tableName.replace(' ', ''), datas)
931
        except Exception as ex:
932
            from App import App
933

    
934
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
935
                                                           sys.exc_info()[-1].tb_lineno)
936
            App.mainWnd().addMessage.emit(MessageType.Error, message)
937

    
938
    '''
939
        @brief      save common code data
940
        @author     kyouho
941
        @date       2018.07.16
942
    '''
943

    
944
    def saveNomialPipeSize(self):
945
        pipeSizes = []
946
        try:
947
            docData = AppDocData.instance()
948

    
949
            from NominalPipeSize import NominalPipeSize
950

    
951
            table = self.ui.tableWidgetNominalDiameter
952
            rowCount = table.rowCount()
953
            for row in range(rowCount):
954
                pipe_size = table.item(row, 0).data(Qt.UserRole)
955
                pipe_size.code = table.item(row, 1).text()
956
                pipe_size.metric = float(table.item(row, 2).text()) if table.item(row, 2).text() != '' else None
957
                pipe_size.inch = float(table.item(row, 3).text()) if table.item(row, 3).text() != '' else None
958
                pipe_size.inchStr = table.item(row, 4).text()
959
                pipe_size.allowable_inch_str = table.item(row, 5).text()
960
                pipe_size.metricStr = table.item(row, 6).text()
961
                pipe_size.allowable_metric_str = table.item(row, 7).text()
962
                pipeSizes.append(pipe_size)
963

    
964
            docData.insertNomialPipeSize(pipeSizes)
965

    
966
        except Exception as ex:
967
            from App import App
968

    
969
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
970
                                                           sys.exc_info()[-1].tb_lineno)
971
            App.mainWnd().addMessage.emit(MessageType.Error, message)
클립보드 이미지 추가 (최대 크기: 500 MB)