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) |