hytos / DTI_PID / DTI_PID / ImportTextFromPDFDialog.py @ 52437c65
이력 | 보기 | 이력해설 | 다운로드 (15.2 KB)
1 |
# coding: utf-8
|
---|---|
2 |
"""This is text importing module from AutoCAD"""
|
3 |
import os |
4 |
import sys |
5 |
from PyQt5.QtCore import * |
6 |
from PyQt5.QtGui import * |
7 |
from PyQt5.QtWidgets import * |
8 |
from AppDocData import AppDocData |
9 |
import subprocess |
10 |
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree, parse |
11 |
|
12 |
import ImportTextFromPDF_UI |
13 |
|
14 |
|
15 |
class QImportTextFromPDFDialog(QDialog): |
16 |
def __init__(self, parent): |
17 |
QDialog.__init__(self, parent)
|
18 |
|
19 |
self.ui = ImportTextFromPDF_UI.Ui_ImportTextFromPDFDialog()
|
20 |
self.ui.setupUi(self) |
21 |
|
22 |
self._dwgs = []
|
23 |
|
24 |
app_doc_data = AppDocData.instance() |
25 |
|
26 |
configs = app_doc_data.getConfigs('PDF Size', 'Width') |
27 |
self.ui.spinBoxWidth.setValue(int(configs[0].value)) if 1 == len(configs) else \ |
28 |
self.ui.spinBoxWidth.setValue(0) |
29 |
configs = app_doc_data.getConfigs('PDF Size', 'Height') |
30 |
self.ui.spinBoxHeight.setValue(int(configs[0].value)) if 1 == len(configs) else \ |
31 |
self.ui.spinBoxHeight.setValue(0) |
32 |
configs = app_doc_data.getConfigs('PDF Offset', 'X') |
33 |
self.ui.spinBoxTextX.setValue(int(configs[0].value)) if 1 == len(configs) else \ |
34 |
self.ui.spinBoxTextX.setValue(0) |
35 |
configs = app_doc_data.getConfigs('PDF Offset', 'Y') |
36 |
self.ui.spinBoxTextY.setValue(int(configs[0].value)) if 1 == len(configs) else \ |
37 |
self.ui.spinBoxTextY.setValue(0) |
38 |
configs = app_doc_data.getConfigs('PDF Offset', 'Scale') |
39 |
self.ui.doubleSpinBoxScale.setValue(float(configs[0].value)) if 1 == len(configs) else \ |
40 |
self.ui.doubleSpinBoxScale.setValue(0.5) |
41 |
self.text_scale = None |
42 |
|
43 |
self.isAccepted = False |
44 |
|
45 |
self.ui.toolButtonPDF.clicked.connect(self.on_add_PDF_click) |
46 |
self.ui.pushButtonSave.clicked.connect(self.on_save_setting) |
47 |
self.ui.pushButtonImport.clicked.connect(self.on_import_pdf) |
48 |
self.ui.pushButtonClose.clicked.connect(self.close) |
49 |
|
50 |
|
51 |
def on_add_PDF_click(self): |
52 |
"""select drawing files"""
|
53 |
project = AppDocData.instance().getCurrentProject() |
54 |
|
55 |
options = QFileDialog.Options() |
56 |
options |= QFileDialog.DontUseNativeDialog |
57 |
files, _ = QFileDialog.getOpenFileNames(self, self.tr('Import', "Select Drawing File"), |
58 |
project.getDrawingFilePath(), "png files (*.png)", options=options)
|
59 |
if files:
|
60 |
self.ui.lineEditDrawing.setText(files[0]) if len(files) == 1 else \ |
61 |
self.ui.lineEditDrawing.setText(str(len(files)) + ' files') |
62 |
|
63 |
self._dwgs.clear()
|
64 |
for file in files: |
65 |
if os.path.exists(file): |
66 |
self._dwgs.append(file) |
67 |
|
68 |
def on_save_setting(self): |
69 |
"""save setting"""
|
70 |
from AppDocData import Config |
71 |
|
72 |
configs = [] |
73 |
configs.append(Config('PDF Size', 'Width', self.ui.spinBoxWidth.value())) |
74 |
configs.append(Config('PDF Size', 'Height', self.ui.spinBoxHeight.value())) |
75 |
configs.append(Config('PDF Offset', 'X', self.ui.spinBoxTextX.value())) |
76 |
configs.append(Config('PDF Offset', 'Y', self.ui.spinBoxTextY.value())) |
77 |
configs.append(Config('PDF Offset', 'Scale', self.ui.doubleSpinBoxScale.value())) |
78 |
|
79 |
app_doc_data = AppDocData.instance() |
80 |
app_doc_data.saveConfigs(configs) |
81 |
|
82 |
QMessageBox.information(self, self.tr('Information'), self.tr('Successfully saved.')) |
83 |
|
84 |
def on_import_pdf(self): |
85 |
"""import line and text from autocad"""
|
86 |
import XmlGenerator as xg |
87 |
from Area import Area |
88 |
|
89 |
project = AppDocData.instance().getCurrentProject() |
90 |
|
91 |
temp_path = project.getTempPath() |
92 |
drawing_path = project.getDrawingFilePath() |
93 |
id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and |
94 |
(os.path.splitext(f)[1].upper() == '.XML')] |
95 |
|
96 |
self.text_scale = [self.ui.spinBoxWidth.value(), self.ui.spinBoxHeight.value(), self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), self.ui.doubleSpinBoxScale.value()] |
97 |
|
98 |
for _file in self._dwgs: |
99 |
file_name = os.path.splitext(os.path.basename(_file))[0]
|
100 |
matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML'] |
101 |
if matches:
|
102 |
try:
|
103 |
id2_image_file = os.path.join(drawing_path, file_name + '.png')
|
104 |
img = AppDocData.my_imread(file_path=id2_image_file) |
105 |
_width = img.shape[1]
|
106 |
_height = img.shape[0]
|
107 |
|
108 |
symbol_areas = [] |
109 |
pdf_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml') |
110 |
pdf_xml = parse(pdf_xml_path) |
111 |
|
112 |
id2_xml_path = os.path.join(temp_path, matches[0])
|
113 |
id2_xml = parse(id2_xml_path) |
114 |
|
115 |
pdf_xml_root = pdf_xml.getroot() |
116 |
id2_xml_root = id2_xml.getroot() |
117 |
|
118 |
for symbol in id2_xml_root.find('SYMBOLS').iter('SYMBOL'): |
119 |
pt = [float(x) for x in symbol.find('LOCATION').text.split(',')] |
120 |
size = [float(x) for x in symbol.find('SIZE').text.split(',')] |
121 |
area = Area('Size')
|
122 |
area.x, area.y = pt[0], pt[1] |
123 |
area.width, area.height = size[0], size[1] |
124 |
symbol_areas.append(area) |
125 |
|
126 |
"""remove texts from id2 xml file"""
|
127 |
textInfo = id2_xml_root.find(xg.TEXT_INFO_LIST_NODE_NAME) |
128 |
textInfo.clear() |
129 |
|
130 |
noteInfo = id2_xml_root.find(xg.NOTE_TEXT_INFO_LIST_NOTE_NAME) |
131 |
noteInfo.clear() |
132 |
|
133 |
lineNoInfo = id2_xml_root.find(xg.LINE_NOS_NODE_NAME) |
134 |
lineNoInfo.clear() |
135 |
"""up to here"""
|
136 |
|
137 |
"""remove unknowns from id2 xml file"""
|
138 |
unknowns = id2_xml_root.find(xg.UNKNOWNS_NODE_NAME) |
139 |
unknowns.clear() |
140 |
"""up to here"""
|
141 |
|
142 |
"""add text from pdf file to id xml file"""
|
143 |
rects = [] |
144 |
|
145 |
for blk_tbl_record in pdf_xml_root.iter('Text'): |
146 |
position = [float(token) for token in blk_tbl_record.attrib['Position'].split(',')] |
147 |
|
148 |
rect = self.make_text_box(blk_tbl_record, position, _height)
|
149 |
rects.append(rect) |
150 |
|
151 |
nodes = self.texts_to_xml(rects, symbol_areas)
|
152 |
if nodes:
|
153 |
textInfo.extend(nodes) |
154 |
|
155 |
id2_xml.write(id2_xml_path, encoding="utf-8", xml_declaration=True) |
156 |
"""up to here"""
|
157 |
|
158 |
QMessageBox.information(self, self.tr('Information'), self.tr('Importing Success!'), |
159 |
QMessageBox.Close) |
160 |
except Exception as ex: |
161 |
from App import App |
162 |
from AppDocData import MessageType |
163 |
|
164 |
message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
|
165 |
f"{sys.exc_info()[-1].tb_lineno}"
|
166 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
167 |
|
168 |
def make_text_box(self, text_node, position, height): |
169 |
try:
|
170 |
loc = [self.text_scale[2] + position[0], self.text_scale[3] + (height - position[1])] |
171 |
|
172 |
text = text_node.text |
173 |
angle = 0#round(float(text_node.attrib['Angle']), 2) |
174 |
|
175 |
#_height = self.text_scale[1]
|
176 |
_height = round(float(text_node.attrib['Height']), 2) |
177 |
loc[1] += round(_height / 2, 2) |
178 |
_height = round(_height * self.text_scale[4], 2) |
179 |
loc[1] -= round(_height / 2, 2) |
180 |
|
181 |
#_width = round(_height * len(text) * self.text_scale[4])
|
182 |
_width = round(float(text_node.attrib['Width']), 2) |
183 |
|
184 |
allowed_error = 0.01
|
185 |
if abs(angle - 1.57) < allowed_error: |
186 |
_height, _width = _width, _height |
187 |
loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width |
188 |
elif abs(angle - 4.71) < allowed_error: |
189 |
_height, _width = _width, _height |
190 |
loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width |
191 |
|
192 |
rect = QRect(loc[0], loc[1], _width, _height) |
193 |
rect._text = text_node.text |
194 |
|
195 |
return rect
|
196 |
|
197 |
except Exception as ex: |
198 |
message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
|
199 |
f"{sys.exc_info()[-1].tb_lineno}"
|
200 |
|
201 |
print(message) |
202 |
|
203 |
|
204 |
def texts_to_xml(self, rects, symbol_areas): |
205 |
"""try to convert text element to id2 xml"""
|
206 |
import uuid |
207 |
from EngineeringTextItem import QEngineeringTextItem |
208 |
from TextItemFactory import TextItemFactory |
209 |
from TextInfo import TextInfo |
210 |
from CodeTables import CodeTable |
211 |
|
212 |
try:
|
213 |
app_doc_data = AppDocData.instance() |
214 |
factory = TextItemFactory.instance() |
215 |
nodes = [] |
216 |
|
217 |
'''
|
218 |
loc = [self.text_scale[2] + position[0], self.text_scale[3] + position[1]]
|
219 |
|
220 |
text = text_node.text
|
221 |
angle = 0#round(float(text_node.attrib['Angle']), 2)
|
222 |
|
223 |
#_height = self.text_scale[1]
|
224 |
_height = round(float(text_node.attrib['Height']), 2)
|
225 |
loc[1] += round(_height / 2, 2)
|
226 |
_height = round(_height * self.text_scale[4], 2)
|
227 |
loc[1] -= round(_height / 2, 2)
|
228 |
|
229 |
#_width = round(_height * len(text) * self.text_scale[4])
|
230 |
_width = round(float(text_node.attrib['Width']), 2)
|
231 |
|
232 |
allowed_error = 0.01
|
233 |
if abs(angle - 1.57) < allowed_error:
|
234 |
_height, _width = _width, _height
|
235 |
loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
|
236 |
elif abs(angle - 4.71) < allowed_error:
|
237 |
_height, _width = _width, _height
|
238 |
loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
|
239 |
'''
|
240 |
|
241 |
# merge adjacent text box
|
242 |
configs = app_doc_data.getConfigs('Text Recognition', 'Merge Size') |
243 |
mergeSize = int(configs[0].value) if 1 == len(configs) else 10 |
244 |
gap_size = 3
|
245 |
|
246 |
horizontals = rects |
247 |
|
248 |
h_merges = [] |
249 |
for horizontal1 in horizontals: |
250 |
for horizontal2 in horizontals: |
251 |
if horizontal1 is horizontal2: |
252 |
continue
|
253 |
if abs(horizontal1.center().y() - horizontal2.center().y()) < gap_size: |
254 |
l1, l2 = horizontal1.left() - mergeSize, horizontal2.left() - mergeSize |
255 |
r1, r2 = horizontal1.right() + mergeSize, horizontal2.right() + mergeSize |
256 |
l_x_y, s_x_y = [l1, r1], [l2, r2] |
257 |
if not (max(l_x_y) < min(s_x_y) or max(s_x_y) < min(l_x_y)): |
258 |
inserted = False
|
259 |
for merge in h_merges: |
260 |
if horizontal1 in merge and horizontal2 in merge: |
261 |
inserted = True
|
262 |
break
|
263 |
elif horizontal1 in merge and horizontal2 not in merge: |
264 |
merge.append(horizontal2) |
265 |
inserted = True
|
266 |
break
|
267 |
elif horizontal2 in merge and horizontal1 not in merge: |
268 |
merge.append(horizontal1) |
269 |
inserted = True
|
270 |
break
|
271 |
if not inserted: |
272 |
h_merges.append([horizontal1, horizontal2]) |
273 |
|
274 |
for merge in h_merges: |
275 |
for rect in merge: |
276 |
if rect in rects: |
277 |
rects.remove(rect) |
278 |
else:
|
279 |
pass
|
280 |
|
281 |
for merge in h_merges: |
282 |
max_x, max_y, min_x, min_y = 0, 0, sys.maxsize, sys.maxsize |
283 |
for rect in merge: |
284 |
if rect.left() < min_x:
|
285 |
min_x = rect.left() |
286 |
if rect.right() > max_x:
|
287 |
max_x = rect.right() |
288 |
if rect.top() < min_y:
|
289 |
min_y = rect.top() |
290 |
if rect.bottom() > max_y:
|
291 |
max_y = rect.bottom() |
292 |
|
293 |
text = None
|
294 |
for rect in sorted(merge, key=lambda param:param.left()): |
295 |
if text is None: |
296 |
text = rect._text |
297 |
elif text[-1] != '/' and text[-1] != '-' and text[-1] != '.': |
298 |
text = text + ' ' + rect._text
|
299 |
else:
|
300 |
text = text + rect._text |
301 |
|
302 |
rect = QRect(min_x, min_y, max_x - min_x, max_y - min_y) |
303 |
rect._text = text |
304 |
rects.append(rect) |
305 |
# up to here
|
306 |
|
307 |
for rect in rects: |
308 |
node = None
|
309 |
text = rect._text |
310 |
loc = [rect.left(), rect.top()] |
311 |
_width, _height = rect.width(), rect.height() |
312 |
|
313 |
_item = factory.createTextItem(TextInfo(text, 10, 10, 10, 10, 0)) # just for type check |
314 |
|
315 |
item = QEngineeringTextItem() |
316 |
item.setPlainText(text) |
317 |
item.loc = loc |
318 |
item.size = (_width, _height) |
319 |
item.angle = 0
|
320 |
|
321 |
for area in app_doc_data.getAreaList(): |
322 |
if area.contains([loc[0], loc[1]]): |
323 |
item.area = area.name |
324 |
break
|
325 |
|
326 |
# determine instrument text
|
327 |
if item.area != 'None' and item.area != 'Drawing': |
328 |
node = item.toXml() |
329 |
elif item.area == 'Drawing' and type(_item) is not QEngineeringTextItem: |
330 |
node = item.toXml() |
331 |
elif False:#item.area == 'Drawing': |
332 |
app_doc_data.getReplaceTables() |
333 |
app_doc_data.getCustomTables() |
334 |
app_doc_data.loadSymbolAttributeCodeTables() |
335 |
for table in CodeTable.TABLES.values(): |
336 |
if table.find_match_exactly(item.text()):
|
337 |
node = item.toXml() |
338 |
break
|
339 |
|
340 |
if node == None: |
341 |
for area in symbol_areas: |
342 |
if area.contains([loc[0], loc[1]]): |
343 |
node = item.toXml() |
344 |
break
|
345 |
else: # allow all text item |
346 |
node = item.toXml() |
347 |
|
348 |
if node:
|
349 |
nodes.append(node) |
350 |
|
351 |
return nodes
|
352 |
except Exception as ex: |
353 |
from App import App |
354 |
|
355 |
message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
|
356 |
f"{sys.exc_info()[-1].tb_lineno}"
|
357 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
358 |
|
359 |
def close(self): |
360 |
QDialog.reject(self)
|