hytos / DTI_PID / DTI_PID / Shapes / EngineeringTextItem.py @ 19d19912
이력 | 보기 | 이력해설 | 다운로드 (17.2 KB)
1 |
# coding: utf-8
|
---|---|
2 |
import os.path |
3 |
import copy |
4 |
import sys |
5 |
try:
|
6 |
from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal, QObject, QT_VERSION_STR, QRect |
7 |
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QBrush, QPen, QTransform, QFont, QColor, QFontMetrics |
8 |
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QGraphicsItem, QAbstractGraphicsShapeItem, QGraphicsTextItem |
9 |
except ImportError: |
10 |
try:
|
11 |
from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QRect, QObject, QT_VERSION_STR |
12 |
from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QFont, QColor, QFontMetrics |
13 |
except ImportError: |
14 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
15 |
|
16 |
from EngineeringPolylineItem import QEngineeringPolylineItem |
17 |
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
18 |
from AppDocData import * |
19 |
from EngineeringAbstractItem import QEngineeringAbstractItem |
20 |
|
21 |
class QEngineeringTextItem(QGraphicsTextItem, QEngineeringAbstractItem): |
22 |
HIGHLIGHT = '#BC4438'
|
23 |
|
24 |
'''
|
25 |
@history 2018.05.17 Jeongwoo Add self._owner variable
|
26 |
'''
|
27 |
def __init__(self, uid=None, parent=None): |
28 |
import uuid |
29 |
|
30 |
QGraphicsTextItem.__init__(self, parent)
|
31 |
QEngineeringAbstractItem.__init__(self)
|
32 |
|
33 |
self.uid = uuid.uuid4() if uid is None else uid |
34 |
self.loc = None |
35 |
self.size = None |
36 |
self.angle = 0 # angle in radian |
37 |
self.conns = []
|
38 |
self._owner = None |
39 |
self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
|
40 |
self.setAcceptHoverEvents(True) |
41 |
self.setAcceptTouchEvents(True) |
42 |
|
43 |
self.setColor(self._color) |
44 |
self._savedColor = None |
45 |
|
46 |
self.delimiter = '"' |
47 |
|
48 |
self.attribute = '' |
49 |
|
50 |
self.transfer = Transfer()
|
51 |
|
52 |
'''
|
53 |
@brief Get owner
|
54 |
@author Jeongwoo
|
55 |
@date 2018.05.17
|
56 |
'''
|
57 |
@property
|
58 |
def owner(self): |
59 |
return self._owner |
60 |
|
61 |
'''
|
62 |
@brief Set owner
|
63 |
@author Jeongwoo
|
64 |
@date 2018.05.17
|
65 |
@history 2018.05.17 Jeongwoo Add Calling setColor if self._owner is None or not
|
66 |
'''
|
67 |
@owner.setter
|
68 |
def owner(self, value): |
69 |
self._owner = value
|
70 |
|
71 |
if self._owner is None: |
72 |
self._color = self.DEFAULT_COLOR |
73 |
self.setColor(self._color) |
74 |
|
75 |
'''
|
76 |
@brief return text string
|
77 |
@author humkyung
|
78 |
@date 2018.04.16
|
79 |
'''
|
80 |
def text(self): |
81 |
return self.toPlainText() |
82 |
|
83 |
'''
|
84 |
@brief return center position of text
|
85 |
@author humkyung
|
86 |
@date 2018.04.16
|
87 |
'''
|
88 |
def center(self): |
89 |
return self.sceneBoundingRect().center() |
90 |
|
91 |
'''
|
92 |
@brief hover event
|
93 |
@authro humkyung
|
94 |
@date
|
95 |
'''
|
96 |
def hoverEnterEvent(self, event): |
97 |
self.setHightlight()
|
98 |
|
99 |
def hoverLeaveEvent(self, event): |
100 |
self.unsetHightlight()
|
101 |
|
102 |
'''
|
103 |
@brief set highlight
|
104 |
@author kyouho
|
105 |
@date 2018.08.27
|
106 |
'''
|
107 |
def setHightlight(self): |
108 |
if self._savedColor is None: |
109 |
self._savedColor = self.getColor() |
110 |
self.setColor(QEngineeringTextItem.HIGHLIGHT)
|
111 |
|
112 |
self.update()
|
113 |
|
114 |
'''
|
115 |
@brief unset highlight
|
116 |
@author kyouho
|
117 |
@date 2018.08.27
|
118 |
'''
|
119 |
def unsetHightlight(self): |
120 |
self.setColor(self._savedColor) |
121 |
self.update()
|
122 |
|
123 |
def hoverMoveEvent(self, event): |
124 |
pass
|
125 |
|
126 |
'''
|
127 |
@brief remove item when user press delete key
|
128 |
@author humkyung
|
129 |
@date 2018.04.23
|
130 |
@history 2018.05.25 Jeongwoo Seperate delete item method
|
131 |
humkyung 2018.08.18 rotate text when user press 'R'
|
132 |
'''
|
133 |
def keyPressEvent(self, event): |
134 |
if event.key() == Qt.Key_Delete:
|
135 |
self.deleteTextItemFromScene()
|
136 |
elif event.key() == Qt.Key_R:
|
137 |
#degree 0
|
138 |
if 0 == self.angle: |
139 |
self.angle = 1.57 |
140 |
#degree 90
|
141 |
elif (1.57 == self.angle): |
142 |
self.angle = 3.14 |
143 |
#degree 180
|
144 |
elif 3.14 == self.angle: |
145 |
self.angle = 4.71 |
146 |
#degree 270
|
147 |
elif 4.71 == self.angle : |
148 |
self.angle = 0 |
149 |
|
150 |
width = self.size[0] |
151 |
height = self.size[1] |
152 |
self.size = (height, width)
|
153 |
|
154 |
self.rotate()
|
155 |
|
156 |
QGraphicsTextItem.keyPressEvent(self, event)
|
157 |
|
158 |
'''
|
159 |
@brief draw rect when item is selected
|
160 |
@author humkyung
|
161 |
@date 2018.07.08
|
162 |
'''
|
163 |
def drawFocusRect(self, painter): |
164 |
self.focuspen = QPen(Qt.DotLine)
|
165 |
self.focuspen.setColor(Qt.black)
|
166 |
self.focuspen.setWidthF(1.5) |
167 |
hilightColor = QColor(255, 0, 0, 127) |
168 |
painter.setBrush(QBrush(hilightColor)) |
169 |
painter.setPen(self.focuspen)
|
170 |
painter.drawRect(self.boundingRect())
|
171 |
|
172 |
'''
|
173 |
@brief override paint(draw connection points)
|
174 |
@author humkyung
|
175 |
@date 2018.07.08
|
176 |
'''
|
177 |
def paint(self, painter, options=None, widget=None): |
178 |
QGraphicsTextItem.paint(self, painter, options, widget)
|
179 |
if self.isSelected(): |
180 |
self.drawFocusRect(painter)
|
181 |
|
182 |
'''
|
183 |
@brief Delete text item
|
184 |
@author Jeongwoo
|
185 |
@date 2018.05.25
|
186 |
'''
|
187 |
def deleteTextItemFromScene(self): |
188 |
self.transfer.onRemoved.emit(self) |
189 |
self.scene().removeItem(self) |
190 |
|
191 |
'''
|
192 |
@brief Return real item position
|
193 |
@author Jeongwoo
|
194 |
@date 2018.05.25
|
195 |
'''
|
196 |
def boundingRectOnScene(self): |
197 |
rect = self.boundingRect()
|
198 |
rect.moveTo(self.loc[0], self.loc[1]) |
199 |
return rect
|
200 |
|
201 |
'''
|
202 |
@brief Double click event, Show QOcrResultDialog
|
203 |
@author Jeongwoo
|
204 |
@date 18.04.23
|
205 |
@history 18.06.20 Jeongwoo Resize QRect added 1
|
206 |
'''
|
207 |
def mouseDoubleClickEvent(self, event): |
208 |
if event.buttons() == Qt.LeftButton:
|
209 |
from OcrResultDialog import QOcrResultDialog |
210 |
dialog = QOcrResultDialog(None, AppDocData.instance().getCurrentPidSource().getQImageOnRect(QRect(self.loc[0], self.loc[1], self.size[0]+1, self.size[1])) |
211 |
, QRect(self.loc[0], self.loc[1], self.size[0]+1, self.size[1]), True, self.text()) |
212 |
(isAccept, textInfoList) = dialog.showDialog() |
213 |
|
214 |
if isAccept:
|
215 |
scene = self.scene()
|
216 |
scene.removeItem(self)
|
217 |
text = textInfoList[0].getText()
|
218 |
self.setPlainText(text)
|
219 |
self.setDefaultTextColor(Qt.blue)
|
220 |
self.addTextItemToScene(scene)
|
221 |
|
222 |
'''
|
223 |
@brief rotate text
|
224 |
@author humkyung
|
225 |
@date 2018.08.18
|
226 |
'''
|
227 |
def rotate(self): |
228 |
sx = 1
|
229 |
sy = 1
|
230 |
width = self.size[0] |
231 |
height = self.size[1] |
232 |
x = self.loc[0] |
233 |
y = self.loc[1] |
234 |
|
235 |
transform = QTransform() |
236 |
if (1.57 == self.angle) or (4.71 == self.angle): |
237 |
rect = self.boundingRect()
|
238 |
sx = width/rect.height() |
239 |
sy = height/rect.width() |
240 |
|
241 |
transform.translate(x, y) |
242 |
transform.translate(width*0.5, height*0.5) |
243 |
transform.scale(1, sy)
|
244 |
transform.rotateRadians(-self.angle)
|
245 |
transform.translate(-rect.width()*0.5, -rect.height()*0.5) |
246 |
elif 3.14 == self.angle: |
247 |
rect = self.boundingRect()
|
248 |
sx = width/rect.width() |
249 |
sy = height/rect.height() |
250 |
|
251 |
transform.translate(x, y - round((rect.height()-height)*0.5)) |
252 |
transform.scale(sx, 1)
|
253 |
transform.rotateRadians(-self.angle)
|
254 |
transform.translate(-width*0.5, -height*0.5) |
255 |
else:
|
256 |
rect = self.boundingRect()
|
257 |
sx = width/rect.width() |
258 |
sy = height/rect.height() |
259 |
|
260 |
#if '\n' not in text:
|
261 |
transform.translate(x, y - round((rect.height()-height)*0.5)) |
262 |
transform.scale(sx, 1)
|
263 |
|
264 |
self.setTransform(transform)
|
265 |
self.update()
|
266 |
|
267 |
'''
|
268 |
@brief Put text on scene
|
269 |
@author Jeongwoo
|
270 |
@date 18.04.23
|
271 |
@history humkyung 2018.06.30 apply font configuration
|
272 |
'''
|
273 |
def addTextItemToScene(self, scene): |
274 |
try:
|
275 |
docData = AppDocData.instance() |
276 |
configs = docData.getConfigs('Text Style', 'Font Name') |
277 |
fontName = configs[0].value if configs else 'Arial' |
278 |
configs = docData.getConfigs('Text Style', 'Font Size') |
279 |
fontSize = int(configs[0].value) if configs else -1 |
280 |
|
281 |
sx = 1
|
282 |
sy = 1
|
283 |
width = self.size[0] |
284 |
height = self.size[1] |
285 |
x = self.loc[0] |
286 |
y = self.loc[1] |
287 |
rect = None
|
288 |
transform = QTransform() |
289 |
if (1.57 == self.angle) or (4.71 == self.angle): |
290 |
font = QFont(fontName, width*1.2 if fontSize == -1 else fontSize) |
291 |
|
292 |
x_factor = width / QFontMetrics(font).height() |
293 |
y_factor = height / QFontMetrics(font).width(self.text())
|
294 |
factor = min(x_factor, y_factor)
|
295 |
font.setPointSizeF(font.pointSizeF()*factor) |
296 |
|
297 |
self.setFont(font)
|
298 |
rect = self.boundingRect()
|
299 |
sx = width/rect.height() |
300 |
sy = height/rect.width() |
301 |
|
302 |
transform.translate(x, y) |
303 |
transform.translate(width*0.5, height*0.5) |
304 |
transform.scale(1, sy)
|
305 |
transform.rotateRadians(-self.angle)
|
306 |
transform.translate(-rect.width()*0.5, -rect.height()*0.5) |
307 |
elif 3.14 == self.angle: |
308 |
font = QFont(fontName, height*1.2 if fontSize == -1 else fontSize) |
309 |
|
310 |
x_factor = width / QFontMetrics(font).width(self.text())
|
311 |
y_factor = height / QFontMetrics(font).height() |
312 |
factor = min(x_factor, y_factor)
|
313 |
font.setPointSizeF(font.pointSizeF()*factor) |
314 |
|
315 |
self.setFont(font)
|
316 |
rect = self.boundingRect()
|
317 |
sx = width/rect.width() |
318 |
sy = height/rect.height() |
319 |
|
320 |
transform.translate(x, y - round((rect.height()-height)*0.5)) |
321 |
transform.scale(sx, 1)
|
322 |
transform.rotateRadians(-self.angle)
|
323 |
transform.translate(-width*0.5, -height*0.5) |
324 |
else:
|
325 |
font = QFont(fontName, height*1.2 if fontSize == -1 else fontSize) |
326 |
|
327 |
x_factor = width / QFontMetrics(font).width(self.text())
|
328 |
y_factor = height / QFontMetrics(font).height() |
329 |
factor = min(x_factor, y_factor)
|
330 |
font.setPointSizeF(font.pointSizeF()*factor) |
331 |
|
332 |
self.setFont(font)
|
333 |
rect = self.boundingRect()
|
334 |
sx = width/rect.width() |
335 |
sy = height/rect.height() |
336 |
|
337 |
#if '\n' not in text:
|
338 |
transform.translate(x, y - round((rect.height()-height)*0.5)) |
339 |
transform.scale(sx, 1)
|
340 |
|
341 |
self.setTransform(transform)
|
342 |
|
343 |
scene.addItem(self)
|
344 |
except Exception as ex: |
345 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
346 |
|
347 |
'''
|
348 |
@brief get connected items
|
349 |
@author humkyung
|
350 |
@date 2018.04.23
|
351 |
'''
|
352 |
def getConnectedItems(self): |
353 |
visited = [] |
354 |
|
355 |
try:
|
356 |
if 1 == len(self.conns): |
357 |
# iterate connected items
|
358 |
pool = [] |
359 |
visited = [] |
360 |
pool.append(self.conns[0]) |
361 |
while len(pool) > 0: |
362 |
it = pool.pop() |
363 |
visited.append(it) |
364 |
for conn in it.conns: |
365 |
if (conn is not None) and (conn not in visited): pool.append(conn) |
366 |
# up to here
|
367 |
except Exception as ex: |
368 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
369 |
|
370 |
return visited
|
371 |
|
372 |
'''
|
373 |
@brief parse xml code
|
374 |
@author humkyung
|
375 |
@date 2018.09.15
|
376 |
'''
|
377 |
@staticmethod
|
378 |
def fromXml(node): |
379 |
from TextItemFactory import TextItemFactory |
380 |
from AppDocData import AppDocData |
381 |
from QEngineeringNoteItem import QEngineeringNoteItem |
382 |
|
383 |
item = None
|
384 |
|
385 |
try:
|
386 |
location = node.find('LOCATION').text if node.find('LOCATION') is not None else '0,0' |
387 |
x = float(location.split(',')[0]) |
388 |
y = float(location.split(',')[1]) |
389 |
width = float(node.find('WIDTH').text) if node.find('WIDTH') is not None else 0 |
390 |
height = float(node.find('HEIGHT').text) if node.find('HEIGHT') is not None else 0 |
391 |
angle = float(node.find('ANGLE').text) if node.find('ANGLE') is not None else 0 |
392 |
value = node.find('VALUE').text
|
393 |
uid = node.find('UID')
|
394 |
attributeValue = node.find('ATTRIBUTEVALUE')
|
395 |
name = node.find('NAME').text
|
396 |
if name == 'TEXT': |
397 |
item = TextItemFactory.instance().createTextItem(value) |
398 |
if item is not None: |
399 |
item.loc = (x, y) |
400 |
item.size = (width, height) |
401 |
item.angle = angle |
402 |
item.setPlainText(value) |
403 |
elif name == 'NOTE': |
404 |
item = QEngineeringNoteItem() |
405 |
if item is not None: |
406 |
item.loc = (x, y) |
407 |
item.size = (width, height) |
408 |
item.angle = angle |
409 |
item.setPlainText(value) |
410 |
|
411 |
## assign area
|
412 |
if item is not None: |
413 |
if node.find('AREA') is None: |
414 |
appDocData = AppDocData.instance() |
415 |
for area in appDocData.getAreaList(): |
416 |
if area.contains([x, y]):
|
417 |
item.area = area.name |
418 |
break
|
419 |
else:
|
420 |
item.area = node.find('AREA').text
|
421 |
## up to here
|
422 |
except Exception as ex: |
423 |
from App import App |
424 |
from AppDocData import MessageType |
425 |
|
426 |
message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno) |
427 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
428 |
|
429 |
return item
|
430 |
|
431 |
'''
|
432 |
@brief generate xml code
|
433 |
@author humkyung
|
434 |
@date 2018.04.23
|
435 |
@history humkyung 2018.04.27 move to QEngineeringLineNoTextItem
|
436 |
humkyung 2018.05.02 add name as parameter
|
437 |
Jeongwoo 2018.05.30 Change variable [owner] is nullable and Add/Modify attributes
|
438 |
'''
|
439 |
def toXml(self, owner = None, name='TEXT'): |
440 |
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree |
441 |
from EngineeringLineItem import QEngineeringLineItem |
442 |
from SymbolSvgItem import SymbolSvgItem |
443 |
|
444 |
try:
|
445 |
node = Element('ATTRIBUTE')
|
446 |
#if owner is not None:
|
447 |
uidNode = Element('UID')
|
448 |
uidNode.text = str(self.uid) |
449 |
node.append(uidNode) |
450 |
|
451 |
attributeValueNode = Element('ATTRIBUTEVALUE')
|
452 |
attributeValueNode.text = self.attribute
|
453 |
node.append(attributeValueNode) |
454 |
|
455 |
nameNode = Element('NAME')
|
456 |
nameNode.text = name |
457 |
node.append(nameNode) |
458 |
|
459 |
locNode = Element('LOCATION')
|
460 |
locNode.text = '{},{}'.format(self.loc[0], self.loc[1]) |
461 |
node.append(locNode) |
462 |
|
463 |
valueNode = Element('VALUE')
|
464 |
valueNode.text = self.text()
|
465 |
node.append(valueNode) |
466 |
|
467 |
angleNode = Element('ANGLE')
|
468 |
angleNode.text = str(self.angle) |
469 |
node.append(angleNode) |
470 |
|
471 |
widthNode = Element('WIDTH')
|
472 |
widthNode.text = str(self.size[0]) |
473 |
node.append(widthNode) |
474 |
|
475 |
heightNode = Element('HEIGHT')
|
476 |
heightNode.text = str(self.size[1]) |
477 |
node.append(heightNode) |
478 |
|
479 |
areaNode = Element('AREA')
|
480 |
areaNode.text = self.area
|
481 |
node.append(areaNode) |
482 |
except Exception as ex: |
483 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
484 |
return str(self.uid) |
485 |
|
486 |
return node
|
487 |
|
488 |
'''
|
489 |
@brief Set Color. Override QEngineeringAbstractItem's
|
490 |
@author Jeongwoo
|
491 |
@date 2018.05.11
|
492 |
@history humkyung 2018.05.13 update after change color
|
493 |
'''
|
494 |
def setColor(self, color): |
495 |
self._color = color
|
496 |
c = QColor() |
497 |
c.setNamedColor(color) |
498 |
self.setDefaultTextColor(c)
|
499 |
self.update()
|
500 |
|
501 |
def getColor(self): |
502 |
return self._color |
503 |
|
504 |
'''
|
505 |
@brief The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
|
506 |
@author Jeongwoo
|
507 |
@date 2018.06.18
|
508 |
'''
|
509 |
class Transfer(QObject): |
510 |
onRemoved = pyqtSignal(QGraphicsItem) |
511 |
|
512 |
def __init__(self, parent = None): |
513 |
QObject.__init__(self, parent)
|