개정판 d7080855
fixed issue #486:
- Connection Point를 클릭하여 생성 시작
- 마우스 오른쪽 버튼 클릭하거나 Connection Point 클릭하여 생성 완료
- Escape 키를 눌렀을때 생성 취소
- 생성 시 설정한 Linetype 적용
- 생성한 라인의 CONN 설정
DTI_PID/DTI_PID/Commands/CreateSymbolCommand.py | ||
---|---|---|
69 | 69 |
#### lambda param=svg : bind 'svg' object to lambda('param') |
70 | 70 |
#### If case of 'lambda svg=svg:', function uses the 'svg' value bound to lambda |
71 | 71 |
svg.clicked.connect(lambda param=svg: self.resultTreeWidget.findItem(param)) |
72 |
svg.onRemoved.connect(self.resultTreeWidget.itemRemoved) |
|
72 |
svg.transfer.onRemoved.connect(self.resultTreeWidget.itemRemoved)
|
|
73 | 73 |
svg.addSvgItemToScene(self.imageViewer.scene) |
74 | 74 |
for connector in svg.connectors: |
75 | 75 |
self.imageViewer.scene.addItem(connector) |
DTI_PID/DTI_PID/Commands/PlaceLineCommand.py | ||
---|---|---|
1 |
import os.path |
|
2 |
import AbstractCommand |
|
3 |
try: |
|
4 |
from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal, QT_VERSION_STR |
|
5 |
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QCursor, QTransform |
|
6 |
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog |
|
7 |
except ImportError: |
|
8 |
try: |
|
9 |
from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR |
|
10 |
from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QCursor |
|
11 |
except ImportError: |
|
12 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
|
13 |
|
|
14 |
class PlaceLineCommand(AbstractCommand.AbstractCommand): |
|
15 |
onSuccess = pyqtSignal() |
|
16 |
onRejected = pyqtSignal(AbstractCommand.AbstractCommand) |
|
17 |
|
|
18 |
def __init__(self, imageViewer): |
|
19 |
super(PlaceLineCommand, self).__init__(imageViewer) |
|
20 |
self.name = 'PlaceLine' |
|
21 |
self.imageViewer.setCursor(QCursor(Qt.CrossCursor)) |
|
22 |
|
|
23 |
self._polyline = None |
|
24 |
|
|
25 |
''' |
|
26 |
@brief reset command status |
|
27 |
@author humkyung |
|
28 |
@date 2018.07.23 |
|
29 |
''' |
|
30 |
def reset(self): |
|
31 |
self._polyline = None |
|
32 |
|
|
33 |
''' |
|
34 |
@brief place a line |
|
35 |
@author humkyung |
|
36 |
@date 2018.07.23 |
|
37 |
''' |
|
38 |
def execute(self, param): |
|
39 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
|
40 |
from SymbolSvgItem import SymbolSvgItem |
|
41 |
from GraphicsPolylineItem import QGraphicsPolylineItem |
|
42 |
|
|
43 |
event = param[1] |
|
44 |
if 'mousePressEvent' == param[0] and event.button() == Qt.LeftButton: |
|
45 |
if self._polyline is None: |
|
46 |
selected = self.imageViewer.scene.itemAt(param[2], QTransform()) |
|
47 |
if selected is not None and type(selected) is QEngineeringConnectorItem: |
|
48 |
self._polyline = QGraphicsPolylineItem() |
|
49 |
self._polyline._vertices.append(selected.center()) |
|
50 |
self.imageViewer.scene.addItem(self._polyline) |
|
51 |
else: |
|
52 |
try: |
|
53 |
QGraphicsView.mouseReleaseEvent(self.imageViewer, event) |
|
54 |
self._polyline._vertices.append(self._polyline._pt) |
|
55 |
self._polyline.update() |
|
56 |
finally: |
|
57 |
pass |
|
58 |
elif 'mouseReleaseEvent' == param[0] and event.button() == Qt.RightButton: |
|
59 |
if self._polyline is not None: |
|
60 |
self.onSuccess.emit() |
|
61 |
elif 'mouseMoveEvent' == param[0]: |
|
62 |
if self._polyline is not None: |
|
63 |
# get connection point near by mouse point |
|
64 |
pt = (param[2].x(), param[2].y()) |
|
65 |
item = self.imageViewer.scene.itemAt(param[2], QTransform()) |
|
66 |
if (item is not None) and (type(item) is SymbolSvgItem): |
|
67 |
connPt = item.getConnectionPointCloseTo(pt, 5) |
|
68 |
if connPt is not None: pt = connPt |
|
69 |
elif (item is not None) and (type(item) is QEngineeringConnectorItem): |
|
70 |
pt = item.center() |
|
71 |
# up to here |
|
72 |
|
|
73 |
if len(self._polyline._vertices) > 0: |
|
74 |
dx = abs(self._polyline._vertices[-1][0] - pt[0]) |
|
75 |
dy = abs(self._polyline._vertices[-1][1] - pt[1]) |
|
76 |
if dx < dy: |
|
77 |
self._polyline._pt = (self._polyline._vertices[-1][0], pt[1]) |
|
78 |
else: |
|
79 |
self._polyline._pt = (pt[0], self._polyline._vertices[-1][1]) |
|
80 |
else: |
|
81 |
self._polyline._pt = pt |
|
82 |
|
|
83 |
self._polyline.update() |
|
84 |
elif 'keyPressEvent' == param[0]: |
|
85 |
if event.key() == Qt.Key_Escape: |
|
86 |
self.onRejected.emit(self) |
|
87 |
|
|
88 |
self.isTreated = False |
|
89 |
|
|
90 |
def undo(self): |
|
91 |
pass |
|
92 |
|
|
93 |
def redo(self): |
|
94 |
pass |
DTI_PID/DTI_PID/ConfigurationAreaDialog.py | ||
---|---|---|
15 | 15 |
import FenceCommand |
16 | 16 |
|
17 | 17 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes') |
18 |
import QGraphicsPolylineItem
|
|
19 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
18 |
import GraphicsPolylineItem |
|
19 |
from EngineeringLineItem import QEngineeringLineItem |
|
20 | 20 |
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
21 | 21 |
import Configuration_Area_UI |
22 | 22 |
|
DTI_PID/DTI_PID/DTI_PID.py | ||
---|---|---|
34 | 34 |
|
35 | 35 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes') |
36 | 36 |
import QGraphicsPolylineItem |
37 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
37 |
from EngineeringLineItem import QEngineeringLineItem |
|
38 | 38 |
from SymbolSvgItem import SymbolSvgItem |
39 | 39 |
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
40 | 40 |
from AppDocData import AppDocData |
... | ... | |
840 | 840 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
841 | 841 |
from QEngineeringLineNoTextItem import QEngineeringLineNoTextItem |
842 | 842 |
from QEngineeringTextItem import QEngineeringTextItem |
843 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
843 |
from EngineeringLineItem import QEngineeringLineItem |
|
844 | 844 |
from LineDetector import LineDetector |
845 | 845 |
|
846 | 846 |
try: |
DTI_PID/DTI_PID/HMBDialog.py | ||
---|---|---|
15 | 15 |
import FenceCommand |
16 | 16 |
|
17 | 17 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes') |
18 |
import QGraphicsPolylineItem
|
|
19 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
18 |
import GraphicsPolylineItem |
|
19 |
from EngineeringLineItem import QEngineeringLineItem |
|
20 | 20 |
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
21 | 21 |
import Configuration_Area_UI |
22 | 22 |
|
DTI_PID/DTI_PID/LineNoTracer.py | ||
---|---|---|
11 | 11 |
from PyQt5.QtSvg import * |
12 | 12 |
import shapely |
13 | 13 |
from AppDocData import AppDocData |
14 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
14 |
from EngineeringLineItem import QEngineeringLineItem |
|
15 | 15 |
from QEngineeringLineNoTextItem import QEngineeringLineNoTextItem |
16 | 16 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
17 | 17 |
from SymbolSvgItem import SymbolSvgItem |
... | ... | |
36 | 36 |
@author humkyung |
37 | 37 |
''' |
38 | 38 |
def findPrimaryLines(self, lineno): |
39 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
39 |
from EngineeringLineItem import QEngineeringLineItem |
|
40 | 40 |
from QEngineeringRunItem import QEngineeringRunItem |
41 | 41 |
|
42 | 42 |
connectedItems = [] |
... | ... | |
55 | 55 |
@author humkyung |
56 | 56 |
''' |
57 | 57 |
def findSecondaryLines(self, lines): |
58 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
58 |
from EngineeringLineItem import QEngineeringLineItem |
|
59 | 59 |
from QEngineeringRunItem import QEngineeringRunItem |
60 | 60 |
|
61 | 61 |
foundCount = 1 |
... | ... | |
96 | 96 |
humkyung 2018.05.18 set start line's owner before tracing |
97 | 97 |
''' |
98 | 98 |
def execute(self, displayMessage, updateProgress, toler=50): |
99 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
99 |
from EngineeringLineItem import QEngineeringLineItem |
|
100 | 100 |
from SymbolSvgItem import SymbolSvgItem |
101 | 101 |
from QEngineeringRunItem import QEngineeringRunItem |
102 | 102 |
from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem |
... | ... | |
179 | 179 |
humkyung 2018.06.22 order connected objects |
180 | 180 |
''' |
181 | 181 |
def findConnectedObjects(self, startLine, toler): |
182 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
182 |
from EngineeringLineItem import QEngineeringLineItem |
|
183 | 183 |
from SymbolSvgItem import SymbolSvgItem |
184 | 184 |
|
185 | 185 |
pool = [] |
DTI_PID/DTI_PID/MainWindow.py | ||
---|---|---|
24 | 24 |
import AreaZoomCommand |
25 | 25 |
import SelectAttributeCommand |
26 | 26 |
import FenceCommand |
27 |
import PlaceLineCommand |
|
27 | 28 |
|
28 | 29 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes') |
29 |
import QGraphicsPolylineItem |
|
30 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
30 |
from GraphicsPolylineItem import QGraphicsPolylineItem
|
|
31 |
from EngineeringLineItem import QEngineeringLineItem |
|
31 | 32 |
from SymbolSvgItem import SymbolSvgItem |
32 | 33 |
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
33 | 34 |
from QEngineeringTextItem import QEngineeringTextItem |
... | ... | |
128 | 129 |
# connect signals and slots |
129 | 130 |
self.actionClose.triggered.connect(self.close) |
130 | 131 |
self.actionOpen.triggered.connect(self.onOpenImageDrawing) |
131 |
self.actionLine.triggered.connect(self.createLine)
|
|
132 |
self.actionLine.triggered.connect(self.onPlaceLine)
|
|
132 | 133 |
self.actionRecognition.triggered.connect(self.recognize) |
133 | 134 |
self.actionLineRecognition.triggered.connect(self.recognizeLine) |
134 | 135 |
self.actionArea.triggered.connect(self.areaConfiguration) |
... | ... | |
547 | 548 |
svg = SymbolSvgItem(svgPath) |
548 | 549 |
svg.buildItem(newSym.getName(), newSym.getType(), 0, [offsetX, offsetY], [w, h], [float(x) for x in newSym.getOriginalPoint().split(',')], [(float(x.split(',')[0]), float(x.split(',')[1])) for x in newSym.getConnectionPoint().split('/')], newSym.getBaseSymbol(), newSym.getAdditionalSymbol(), newSym.getHasInstrumentLabel) |
549 | 550 |
|
550 |
svg.onRemoved.connect(self.resultTreeWidget.itemRemoved) |
|
551 |
svg.transfer.onRemoved.connect(self.resultTreeWidget.itemRemoved)
|
|
551 | 552 |
svg.addSvgItemToScene(self.graphicsView.scene) |
552 | 553 |
for connector in svg.connectors: |
553 | 554 |
self.graphicsView.scene.addItem(connector) |
... | ... | |
556 | 557 |
QApplication.restoreOverrideCursor() |
557 | 558 |
|
558 | 559 |
''' |
559 |
@brief create a line |
|
560 |
@author humkyung |
|
561 |
@history 2018.05.10 Jeongwoo Change method for Checkable action
|
|
560 |
@brief create a line
|
|
561 |
@author humkyung
|
|
562 |
@history Jeongwoo 2018.05.10 Change method for Checkable action
|
|
562 | 563 |
''' |
563 |
def createLine(self):
|
|
564 |
def onPlaceLine(self):
|
|
564 | 565 |
if not self.graphicsView.hasImage(): |
565 | 566 |
self.actionLine.setChecked(False) |
566 | 567 |
self.showImageSelectionMessageBox() |
567 | 568 |
return |
568 |
if self.actionLine.isChecked(): |
|
569 |
self.graphicsView.command = CreateCommand.CreateCommand(self.graphicsView, QEngineeringLineItem()) |
|
570 |
else: |
|
571 |
self.graphicsView.useDefaultCommand() |
|
569 |
|
|
570 |
self.actionLine.setChecked(True) |
|
571 |
if not hasattr(self.actionLine, 'tag'): |
|
572 |
self.actionLine.tag = PlaceLineCommand.PlaceLineCommand(self.graphicsView) |
|
573 |
self.actionLine.tag.onSuccess.connect(self.onLineCreated) |
|
574 |
self.actionLine.tag.onRejected.connect(self.onCommandRejected) |
|
575 |
|
|
576 |
self.graphicsView.command = self.actionLine.tag |
|
572 | 577 |
|
573 | 578 |
''' |
579 |
@brief add created lines to scene |
|
580 |
@author humkyung |
|
581 |
@date 2018.07.23 |
|
582 |
''' |
|
583 |
def onLineCreated(self): |
|
584 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
|
585 |
|
|
586 |
try: |
|
587 |
count = len(self.actionLine.tag._polyline._vertices) |
|
588 |
if count > 1: |
|
589 |
items = [] |
|
590 |
|
|
591 |
lineType = self.lineComboBox.currentText() |
|
592 |
for index in range(count - 1): |
|
593 |
start = self.actionLine.tag._polyline._vertices[index] |
|
594 |
end = self.actionLine.tag._polyline._vertices[index + 1] |
|
595 |
|
|
596 |
lineItem = QEngineeringLineItem(vertices=[start, end]) |
|
597 |
lineItem.transfer.onRemoved.connect(self.itemRemoved) |
|
598 |
lineItem.buildItem() |
|
599 |
lineItem.lineType = lineType |
|
600 |
if items: |
|
601 |
lineItem.conns[0] = items[-1] |
|
602 |
items[-1].conns[1] = lineItem |
|
603 |
else: |
|
604 |
pt = lineItem.startPoint() |
|
605 |
selected = self.graphicsView.scene.itemAt(QPointF(pt[0], pt[1]), QTransform()) |
|
606 |
if selected is not None and type(selected) is QEngineeringConnectorItem: |
|
607 |
lineItem.conns[0] = selected.parent |
|
608 |
|
|
609 |
items.append(lineItem) |
|
610 |
self.graphicsView.scene.addItem(lineItem) |
|
611 |
|
|
612 |
pt = items[-1].endPoint() |
|
613 |
selected = self.graphicsView.scene.itemAt(QPointF(pt[0], pt[1]), QTransform()) |
|
614 |
if selected is not None and type(selected) is QEngineeringConnectorItem: |
|
615 |
items[-1].conns[1] = selected.parent |
|
616 |
finally: |
|
617 |
self.graphicsView.scene.removeItem(self.actionLine.tag._polyline) |
|
618 |
self.actionLine.tag.reset() |
|
619 |
|
|
620 |
''' |
|
621 |
@brief refresh scene |
|
622 |
@author humkyung |
|
623 |
@date 2018.07.23 |
|
624 |
''' |
|
625 |
def onCommandRejected(self, cmd): |
|
626 |
try: |
|
627 |
if cmd is self.actionLine.tag: |
|
628 |
self.graphicsView.scene.removeItem(self.actionLine.tag._polyline) |
|
629 |
self.graphicsView.scene.update() |
|
630 |
self.actionLine.tag.reset() |
|
631 |
|
|
632 |
self.actionLine.setChecked(False) |
|
633 |
finally: |
|
634 |
self.graphicsView.useDefaultCommand() |
|
635 |
|
|
636 |
''' |
|
574 | 637 |
@brief recognize symbol and text |
575 | 638 |
@author humkyung |
576 | 639 |
@date 2018.04.?? |
... | ... | |
701 | 764 |
searchedMap.append((symbol, svg)) |
702 | 765 |
# up to here |
703 | 766 |
|
704 |
svg.onRemoved.connect(self.itemRemoved) |
|
767 |
svg.transfer.onRemoved.connect(self.itemRemoved)
|
|
705 | 768 |
self.addSvgItemToScene(svg) |
706 | 769 |
for connector in svg.connectors: |
707 | 770 |
self.graphicsView.scene.addItem(connector) |
... | ... | |
752 | 815 |
item.size = (width, height) |
753 | 816 |
item.angle = angle |
754 | 817 |
item.setPlainText(text) |
755 |
item.onRemoved.connect(self.itemRemoved) |
|
818 |
item.transfer.onRemoved.connect(self.itemRemoved)
|
|
756 | 819 |
self.addTextItemToScene(item) |
757 | 820 |
except Exception as ex: |
758 | 821 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
... | ... | |
788 | 851 |
''' |
789 | 852 |
def drawUnknownItems(self): |
790 | 853 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
791 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
854 |
from EngineeringLineItem import QEngineeringLineItem |
|
792 | 855 |
from QEngineeringUnknownItem import QEngineeringUnknownItem |
793 | 856 |
|
794 | 857 |
try: |
... | ... | |
861 | 924 |
for symbol in root.iter('SYMBOL'): |
862 | 925 |
item = SymbolSvgItem.fromXml(symbol) |
863 | 926 |
if item[0] is not None: |
864 |
item[0].onRemoved.connect(self.itemRemoved) |
|
927 |
item[0].transfer.onRemoved.connect(self.itemRemoved)
|
|
865 | 928 |
symbols.append(item) |
866 | 929 |
else: |
867 | 930 |
pt = [float(x) for x in symbol.find('LOCATION').text.split(',')] |
... | ... | |
922 | 985 |
item.size = (width, height) |
923 | 986 |
item.angle = angle |
924 | 987 |
item.setPlainText(value) |
925 |
item.onRemoved.connect(self.itemRemoved) |
|
988 |
item.transfer.onRemoved.connect(self.itemRemoved)
|
|
926 | 989 |
self.addTextItemToScene(item) |
927 | 990 |
elif name == 'NOTE': |
928 | 991 |
item = TextItemFactory.instance().createTextItem(value) |
... | ... | |
948 | 1011 |
item.size = (width, height) |
949 | 1012 |
item.angle = angle |
950 | 1013 |
item.setPlainText(text) |
951 |
item.onRemoved.connect(self.itemRemoved) |
|
1014 |
item.transfer.onRemoved.connect(self.itemRemoved)
|
|
952 | 1015 |
self.addTextItemToScene(item) |
953 | 1016 |
|
954 | 1017 |
for line in root.iter('LINE'): |
DTI_PID/DTI_PID/QConnectAttrDialog.py | ||
---|---|---|
8 | 8 |
import cv2 |
9 | 9 |
from AppDocData import AppDocData |
10 | 10 |
from LineDetector import LineDetector |
11 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
11 |
from EngineeringLineItem import QEngineeringLineItem |
|
12 | 12 |
from QEngineeringLineNoTextItem import QEngineeringLineNoTextItem |
13 | 13 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
14 | 14 |
from SymbolSvgItem import SymbolSvgItem |
DTI_PID/DTI_PID/QRecognitionDialog.py | ||
---|---|---|
8 | 8 |
import cv2 |
9 | 9 |
from AppDocData import AppDocData |
10 | 10 |
from LineDetector import LineDetector |
11 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
11 |
from EngineeringLineItem import QEngineeringLineItem |
|
12 | 12 |
from QEngineeringLineNoTextItem import QEngineeringLineNoTextItem |
13 | 13 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
14 | 14 |
from SymbolSvgItem import SymbolSvgItem |
DTI_PID/DTI_PID/QtImageViewer.py | ||
---|---|---|
16 | 16 |
import DefaultCommand |
17 | 17 |
|
18 | 18 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes') |
19 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
19 |
from EngineeringLineItem import QEngineeringLineItem |
|
20 | 20 |
from SymbolSvgItem import SymbolSvgItem |
21 | 21 |
|
22 | 22 |
__author__ = "Marcel Goldschen-Ohm <marcel.goldschen@gmail.com>" |
... | ... | |
337 | 337 |
QGraphicsView.mouseDoubleClickEvent(self, event) |
338 | 338 |
|
339 | 339 |
''' |
340 |
@brief key press event |
|
341 |
@author Jeongwoo |
|
342 |
@date 2018.??.?? |
|
340 |
@brief key press event |
|
341 |
@author Jeongwoo |
|
342 |
@date 2018.??.?? |
|
343 |
@history send escape key event to command |
|
343 | 344 |
''' |
344 | 345 |
def keyPressEvent(self, event): |
345 |
if event.key() == Qt.Key_Control: |
|
346 |
self.isPressCtrl = True |
|
347 |
elif event.key() == Qt.Key_Delete: |
|
348 |
for item in self.scene.selectedItems(): |
|
349 |
#self.scene.removeItem(item) |
|
350 |
item.onRemoved.emit(item) |
|
351 |
pass |
|
352 |
|
|
353 |
QGraphicsView.keyPressEvent(self, event) |
|
346 |
try: |
|
347 |
if event.key() == Qt.Key_Control: |
|
348 |
self.isPressCtrl = True |
|
349 |
elif event.key() == Qt.Key_Delete: |
|
350 |
for item in self.scene.selectedItems(): |
|
351 |
item.transfer.onRemoved.emit(item) |
|
352 |
elif event.key() == Qt.Key_Escape: |
|
353 |
if self.command is not None: |
|
354 |
self.command.execute(['keyPressEvent', event]) |
|
355 |
|
|
356 |
QGraphicsView.keyPressEvent(self, event) |
|
357 |
except Exception as ex: |
|
358 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
354 | 359 |
|
355 | 360 |
''' |
356 | 361 |
@brief key release event |
DTI_PID/DTI_PID/READ ME.txt | ||
---|---|---|
72 | 72 |
[3] ���� ������ ������ �ϳ��� ���� |
73 | 73 |
[4] ���ΰ� �ɺ��� ���� |
74 | 74 |
[5] ���ΰ� ������ ���� |
75 |
[6] �� ������ QEngineeringLineItem ���� ���� �� QtImageViewer ���� �߰�
|
|
75 |
[6] �� ������ EngineeringLineItem ���� ���� �� QtImageViewer ���� �߰� |
|
76 | 76 |
[7] ȭ��ǥ �߰� |
77 | 77 |
|
78 | 78 |
7) MainWindow.py |
DTI_PID/DTI_PID/ResultPropertyTableWidget.py | ||
---|---|---|
16 | 16 |
import math |
17 | 17 |
from SymbolSvgItem import SymbolSvgItem |
18 | 18 |
from QEngineeringLineNoTextItem import QEngineeringLineNoTextItem |
19 |
from QEngineeringLineItem import QEngineeringLineItem
|
|
19 |
from EngineeringLineItem import QEngineeringLineItem |
|
20 | 20 |
from QEngineeringNoteItem import QEngineeringNoteItem |
21 | 21 |
from AppDocData import AppDocData |
22 | 22 |
from Drawing import Drawing |
DTI_PID/DTI_PID/Shapes/EngineeringConnectorItem.py | ||
---|---|---|
46 | 46 |
self.setZValue(self.parent.zValue() + 1) |
47 | 47 |
|
48 | 48 |
''' |
49 |
@brief return center of connector |
|
50 |
@author humkyung |
|
51 |
@date 2018.07.23 |
|
52 |
''' |
|
53 |
def center(self): |
|
54 |
pt = self.sceneBoundingRect().center() |
|
55 |
return (pt.x(), pt.y()) |
|
56 |
|
|
57 |
''' |
|
49 | 58 |
@brief set position of connector |
50 | 59 |
@author humkyung |
51 | 60 |
@date 2018.05.02 |
DTI_PID/DTI_PID/Shapes/EngineeringLineItem.py | ||
---|---|---|
1 |
# coding: utf-8 |
|
2 |
import sys |
|
3 |
import os.path |
|
4 |
import copy |
|
5 |
import cv2 |
|
6 |
|
|
7 |
try: |
|
8 |
from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal, QT_VERSION_STR, QRect |
|
9 |
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QColor, QBrush, QPen, QTransform |
|
10 |
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QGraphicsItem, QAbstractGraphicsShapeItem, QGraphicsPathItem |
|
11 |
except ImportError: |
|
12 |
try: |
|
13 |
from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR, QRect |
|
14 |
from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog |
|
15 |
except ImportError: |
|
16 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
|
17 |
|
|
18 |
from GraphicsPolylineItem import QGraphicsPolylineItem |
|
19 |
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
|
20 |
import shapely |
|
21 |
|
|
22 |
class QEngineeringLineItem(QGraphicsPolylineItem): |
|
23 |
''' |
|
24 |
@history 2018.05.11 Jeongwoo Make Comments self.setPen() |
|
25 |
2018.05.15 Jeongwoo Change method to call parent's __init__ |
|
26 |
humkyung 2018.06.21 add vertices to parameter |
|
27 |
''' |
|
28 |
def __init__(self, vertices=[], parent=None): |
|
29 |
QGraphicsPolylineItem.__init__(self, parent) |
|
30 |
self.isCreated = True |
|
31 |
|
|
32 |
self.conns = [None, None] |
|
33 |
self._owner = None |
|
34 |
self._flowMark = None |
|
35 |
self._lineType = 'Primary' |
|
36 |
|
|
37 |
# add vertex |
|
38 |
for vertex in vertices: |
|
39 |
self._pol.append(QPointF(vertex[0], vertex[1])) |
|
40 |
# up to here |
|
41 |
|
|
42 |
self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable) |
|
43 |
|
|
44 |
self.setAcceptHoverEvents(True) |
|
45 |
self.setAcceptTouchEvents(True) |
|
46 |
|
|
47 |
ptStart = self.startPoint() |
|
48 |
ptEnd = self.endPoint() |
|
49 |
self.setToolTip('({},{})-({},{})'.format(ptStart[0], ptStart[1], ptEnd[0], ptEnd[1])) |
|
50 |
|
|
51 |
''' |
|
52 |
@breif getter owner |
|
53 |
@author humkyung |
|
54 |
@date 2018.05.10 |
|
55 |
''' |
|
56 |
@property |
|
57 |
def owner(self): |
|
58 |
return self._owner |
|
59 |
|
|
60 |
''' |
|
61 |
@brief setter owner |
|
62 |
@author humkyung |
|
63 |
@date 2018.05.10 |
|
64 |
@history 2018.05.17 Jeongwoo Add Calling setColor |
|
65 |
''' |
|
66 |
@owner.setter |
|
67 |
def owner(self, value): |
|
68 |
self._owner = value |
|
69 |
|
|
70 |
if self._owner is None: |
|
71 |
self._color = self.DEFAULT_COLOR |
|
72 |
self.setColor(self._color) |
|
73 |
|
|
74 |
''' |
|
75 |
@brief getter flow mark |
|
76 |
@author humkyung |
|
77 |
@date 2018.06.21 |
|
78 |
''' |
|
79 |
@property |
|
80 |
def flowMark(self): |
|
81 |
return self._flowMark |
|
82 |
|
|
83 |
''' |
|
84 |
@brief setter flow mark |
|
85 |
@author humkyung |
|
86 |
@date 2018.06.21 |
|
87 |
''' |
|
88 |
@flowMark.setter |
|
89 |
def flowMark(self, value): |
|
90 |
self._flowMark = value |
|
91 |
|
|
92 |
''' |
|
93 |
@brief getter of lineType |
|
94 |
@author humkyung |
|
95 |
@date 2018.06.27 |
|
96 |
''' |
|
97 |
@property |
|
98 |
def lineType(self): |
|
99 |
return self._lineType |
|
100 |
|
|
101 |
''' |
|
102 |
@brief setter of lineType |
|
103 |
@author humkyung |
|
104 |
@date 2018.06.27 |
|
105 |
''' |
|
106 |
@lineType.setter |
|
107 |
def lineType(self, value): |
|
108 |
from AppDocData import AppDocData |
|
109 |
|
|
110 |
self._lineType = value |
|
111 |
|
|
112 |
docData = AppDocData.instance() |
|
113 |
config = docData.getLineTypeConfig(self._lineType) |
|
114 |
|
|
115 |
_pen = self.pen() |
|
116 |
_pen.setWidth(config[1]) |
|
117 |
_pen.setStyle(config[2]) |
|
118 |
self.setPen(_pen) |
|
119 |
self.update() |
|
120 |
|
|
121 |
''' |
|
122 |
@brief clone an object |
|
123 |
''' |
|
124 |
def clone(self): |
|
125 |
clone = QEngineeringLineItem() |
|
126 |
clone._vertices = copy.deepcopy(self._vertices) |
|
127 |
for vertex in clone._vertices: |
|
128 |
clone._pol.append(QPointF(vertex[0], vertex[1])) |
|
129 |
clone.buildItem() |
|
130 |
clone.isCreated = self.isCreated |
|
131 |
|
|
132 |
return clone |
|
133 |
|
|
134 |
''' |
|
135 |
@brief dot product of given two vectors |
|
136 |
@author humkyung |
|
137 |
@date 2018.04.14 |
|
138 |
''' |
|
139 |
def dotProduct(self, lhs, rhs): |
|
140 |
return sum([lhs[i]*rhs[i] for i in range(len(lhs))]) |
|
141 |
|
|
142 |
''' |
|
143 |
@brief distance between line and point |
|
144 |
@author humkyung |
|
145 |
@date 2018.04.16 |
|
146 |
''' |
|
147 |
def distanceTo(self, pt): |
|
148 |
from shapely.geometry import Point, LineString |
|
149 |
|
|
150 |
startPt = self.startPoint() |
|
151 |
endPt = self.endPoint() |
|
152 |
line = LineString([(startPt[0], startPt[1]), (endPt[0], endPt[1])]) |
|
153 |
dist = line.distance(Point(pt[0], pt[1])) |
|
154 |
|
|
155 |
return dist |
|
156 |
|
|
157 |
''' |
|
158 |
@brief return perpendicular vector |
|
159 |
@author humkyung |
|
160 |
@date 2018.04.21 |
|
161 |
''' |
|
162 |
def perpendicular(self): |
|
163 |
import math |
|
164 |
|
|
165 |
dx = self.endPoint()[0] - self.startPoint()[0] |
|
166 |
dy = self.endPoint()[1] - self.startPoint()[1] |
|
167 |
dx,dy = -dy,dx |
|
168 |
length = math.sqrt(dx*dx + dy*dy) |
|
169 |
dx /= length |
|
170 |
dy /= length |
|
171 |
|
|
172 |
return (dx,dy) |
|
173 |
|
|
174 |
''' |
|
175 |
@brief return angle of line in radian |
|
176 |
@author humkyung |
|
177 |
@date 2018.04.22 |
|
178 |
''' |
|
179 |
def angle(self): |
|
180 |
import math |
|
181 |
|
|
182 |
startPt = self.startPoint() |
|
183 |
endPt = self.endPoint() |
|
184 |
dx = endPt[0] - startPt[0] |
|
185 |
dy = endPt[1] - startPt[1] |
|
186 |
dot = self.dotProduct((1,0), (dx, dy)) |
|
187 |
length = math.sqrt(dx*dx + dy*dy) |
|
188 |
return math.acos(dot/length) |
|
189 |
|
|
190 |
''' |
|
191 |
@brief return length of line |
|
192 |
@author humkyung |
|
193 |
@date 2018.05.08 |
|
194 |
''' |
|
195 |
def length(self): |
|
196 |
import math |
|
197 |
|
|
198 |
startPt = self.startPoint() |
|
199 |
endPt = self.endPoint() |
|
200 |
dx = endPt[0] - startPt[0] |
|
201 |
dy = endPt[1] - startPt[1] |
|
202 |
return math.sqrt(dx*dx + dy*dy) |
|
203 |
|
|
204 |
''' |
|
205 |
@brief check if line is horizontal |
|
206 |
@author humkyung |
|
207 |
@date 2018.04.27 |
|
208 |
''' |
|
209 |
def isHorizontal(self): |
|
210 |
import math |
|
211 |
|
|
212 |
startPt = self.startPoint() |
|
213 |
endPt = self.endPoint() |
|
214 |
dx = endPt[0] - startPt[0] |
|
215 |
dy = endPt[1] - startPt[1] |
|
216 |
|
|
217 |
return math.fabs(dx) > math.fabs(dy) |
|
218 |
|
|
219 |
''' |
|
220 |
@brief check if line is vertical |
|
221 |
@author humkyung |
|
222 |
@date 2018.04.27 |
|
223 |
''' |
|
224 |
def isVertical(self): |
|
225 |
import math |
|
226 |
|
|
227 |
startPt = self.startPoint() |
|
228 |
endPt = self.endPoint() |
|
229 |
dx = endPt[0] - startPt[0] |
|
230 |
dy = endPt[1] - startPt[1] |
|
231 |
|
|
232 |
return math.fabs(dy) > math.fabs(dx) |
|
233 |
|
|
234 |
''' |
|
235 |
@brief get intersection point between this and given line |
|
236 |
@author humkyung |
|
237 |
@date 2018.04.21 |
|
238 |
@history Jeongwoo 2018.05.15 Add normalize |
|
239 |
Jeongwoo 2018.05.16 Add length == 0 check |
|
240 |
''' |
|
241 |
def intersection(self, line): |
|
242 |
import math |
|
243 |
from shapely.geometry import Point, LineString |
|
244 |
|
|
245 |
startPt = self.startPoint() |
|
246 |
endPt = self.endPoint() |
|
247 |
dx = endPt[0] - startPt[0] |
|
248 |
dy = endPt[1] - startPt[1] |
|
249 |
length = math.sqrt(dx*dx + dy*dy) |
|
250 |
if length == 0: |
|
251 |
return None |
|
252 |
dx /= length |
|
253 |
dy /= length |
|
254 |
lhs = LineString([(startPt[0] - dx*20, startPt[1] - dy*20), (endPt[0] + dx*20, endPt[1] + dy*20)]) |
|
255 |
rhs = LineString(line) |
|
256 |
return lhs.intersection(rhs) |
|
257 |
|
|
258 |
''' |
|
259 |
@brief check if two lines are connectable |
|
260 |
@author humkyung |
|
261 |
@date 2018.05.12 |
|
262 |
@history Jeongwoo 18.05.15 Add check pt's type |
|
263 |
Jeongwoo 18.05.16 Add length == 0 check |
|
264 |
''' |
|
265 |
def isConnectable(self, line, toler=20): |
|
266 |
import math |
|
267 |
|
|
268 |
startPt = line.startPoint() |
|
269 |
endPt = line.endPoint() |
|
270 |
|
|
271 |
dx = endPt[0] - startPt[0] |
|
272 |
dy = endPt[1] - startPt[1] |
|
273 |
length = math.sqrt(dx*dx + dy*dy) |
|
274 |
if length == 0: |
|
275 |
return False |
|
276 |
dx /= length |
|
277 |
dy /= length |
|
278 |
extendedLine = [(startPt[0] - dx*toler, startPt[1] - dy*toler), (endPt[0] + dx*toler, endPt[1] + dy*toler)] |
|
279 |
pt = self.intersection(extendedLine) |
|
280 |
|
|
281 |
return (pt is not None) and (type(pt) == shapely.geometry.point.Point) |
|
282 |
|
|
283 |
''' |
|
284 |
@brief check if line and given item is jointable |
|
285 |
@author humkyung |
|
286 |
@date 2018.06.26 |
|
287 |
@history humkyung 2018.07.03 allow item to be line or symbol |
|
288 |
''' |
|
289 |
def isJointed(self, item, toler=5): |
|
290 |
import math |
|
291 |
from SymbolSvgItem import SymbolSvgItem |
|
292 |
|
|
293 |
lhs = [self.startPoint(), self.endPoint()] |
|
294 |
if type(item) is QEngineeringLineItem: |
|
295 |
rhs = [item.startPoint(), item.endPoint()] |
|
296 |
elif issubclass(type(item), SymbolSvgItem): |
|
297 |
rhs = item.connPts |
|
298 |
else: |
|
299 |
rhs = [] |
|
300 |
|
|
301 |
for pt in lhs: |
|
302 |
for _pt in rhs: |
|
303 |
dx = _pt[0] - pt[0] |
|
304 |
dy = _pt[1] - pt[1] |
|
305 |
if math.sqrt(dx*dx + dy*dy) < toler: |
|
306 |
return True |
|
307 |
|
|
308 |
return False |
|
309 |
|
|
310 |
''' |
|
311 |
@brief arrange vertex order |
|
312 |
@author humkyung |
|
313 |
@date 2018.07.04 |
|
314 |
''' |
|
315 |
def arrangeVertexOrder(self, arranged): |
|
316 |
import math |
|
317 |
|
|
318 |
lhs = [arranged.startPoint(), arranged.endPoint()] |
|
319 |
rhs = [self.startPoint(), self.endPoint()] |
|
320 |
|
|
321 |
index = 0 |
|
322 |
indexed = 0 |
|
323 |
minDist = None |
|
324 |
for pt in lhs: |
|
325 |
for _pt in rhs: |
|
326 |
index += 1 |
|
327 |
dx = _pt[0] - pt[0] |
|
328 |
dy = _pt[1] - pt[1] |
|
329 |
dist = math.sqrt(dx*dx + dy*dy) |
|
330 |
if minDist is None or dist < minDist: |
|
331 |
minDist = dist |
|
332 |
indexed = index |
|
333 |
|
|
334 |
if indexed == 1 or indexed == 4: |
|
335 |
self.reverse() |
|
336 |
|
|
337 |
''' |
|
338 |
@brief check if two lines are extendable |
|
339 |
@author humkyung |
|
340 |
@date 2018.06.25 |
|
341 |
@history humkyung 2018.06.27 check line type |
|
342 |
''' |
|
343 |
def isExtendable(self, line, toler=5): |
|
344 |
import math |
|
345 |
from SymbolSvgItem import SymbolSvgItem |
|
346 |
|
|
347 |
if self.lineType == line.lineType: |
|
348 |
if self.isHorizontal() and line.isHorizontal(): |
|
349 |
flag = (line.conns[0] is not None and issubclass(type(line.conns[0]), SymbolSvgItem)) or (line.conns[1] is not None and issubclass(type(line.conns[1]), SymbolSvgItem)) |
|
350 |
return (flag and (math.fabs(self.startPoint()[1] - line.startPoint()[1]) < toler)) |
|
351 |
elif self.isVertical() and line.isVertical(): |
|
352 |
flag = (line.conns[0] is not None and issubclass(type(line.conns[0]), SymbolSvgItem)) or (line.conns[1] is not None and issubclass(type(line.conns[1]), SymbolSvgItem)) |
|
353 |
return (flag and (math.fabs(self.startPoint()[0] - line.startPoint()[0]) < toler)) |
|
354 |
|
|
355 |
return False |
|
356 |
|
|
357 |
''' |
|
358 |
@brief connect line and symbol is able to be connected and return symbol |
|
359 |
@author humkyung |
|
360 |
@date 2018.04.16 |
|
361 |
@history humkyung 2018.05.08 check if line is possible to be connected |
|
362 |
Jeongwoo 2018.05.15 Split if-statement and Connect each symbol and line |
|
363 |
''' |
|
364 |
def connectIfPossible(self, obj, toler): |
|
365 |
from shapely.geometry import Point |
|
366 |
from SymbolSvgItem import SymbolSvgItem |
|
367 |
res = [] |
|
368 |
|
|
369 |
startPt = self.startPoint() |
|
370 |
endPt = self.endPoint() |
|
371 |
|
|
372 |
if issubclass(type(obj), SymbolSvgItem): |
|
373 |
for i in range(len(obj.connPts)): |
|
374 |
pt = obj.connPts[i] |
|
375 |
if len(obj.conns) <= i: obj.conns.append(None) |
|
376 |
|
|
377 |
if (self.conns[0] is None) and (Point(startPt[0], startPt[1]).distance(Point(pt[0], pt[1])) < toler): |
|
378 |
self.conns[0] = obj |
|
379 |
obj.conns[i] = self |
|
380 |
res.append(obj) |
|
381 |
if (self.conns[1] is None) and (Point(endPt[0], endPt[1]).distance(Point(pt[0], pt[1])) < toler): |
|
382 |
self.conns[1] = obj |
|
383 |
obj.conns[i] = self |
|
384 |
res.append(obj) |
|
385 |
elif type(obj) is QEngineeringLineItem: |
|
386 |
_startPt = obj.startPoint() |
|
387 |
_endPt = obj.endPoint() |
|
388 |
if((Point(startPt[0], startPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)): |
|
389 |
self.conns[0] = obj |
|
390 |
obj.conns[0] = self |
|
391 |
res.append(obj) |
|
392 |
if ((Point(startPt[0], startPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)): |
|
393 |
self.conns[0] = obj |
|
394 |
obj.conns[1] = self |
|
395 |
res.append(obj) |
|
396 |
|
|
397 |
if((Point(endPt[0], endPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)): |
|
398 |
self.conns[1] = obj |
|
399 |
obj.conns[0] = self |
|
400 |
res.append(obj) |
|
401 |
|
|
402 |
if ((Point(endPt[0], endPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)): |
|
403 |
self.conns[1] = obj |
|
404 |
obj.conns[1] = self |
|
405 |
res.append(obj) |
|
406 |
|
|
407 |
return res |
|
408 |
|
|
409 |
''' |
|
410 |
@brief reverse line |
|
411 |
@author humkyung |
|
412 |
@date 2018.07.03 |
|
413 |
''' |
|
414 |
def reverse(self): |
|
415 |
clone = self._pol.toPolygon() |
|
416 |
self._pol.clear() |
|
417 |
for idx in reversed(range(clone.count())): |
|
418 |
self._pol.append(clone.at(idx)) |
|
419 |
|
|
420 |
''' |
|
421 |
@brief add flow arrow |
|
422 |
@author humkyung |
|
423 |
@date 2018.05.08 |
|
424 |
@history 2018.05.24 Jeongwoo Modifying Draw Flow Arrow |
|
425 |
''' |
|
426 |
def addFlowArrow(self): |
|
427 |
import numpy as np |
|
428 |
import cv2 |
|
429 |
import math |
|
430 |
import sys |
|
431 |
global src |
|
432 |
from shapely.geometry import Point |
|
433 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
|
434 |
from AppDocData import AppDocData |
|
435 |
|
|
436 |
try: |
|
437 |
docData = AppDocData.instance() |
|
438 |
area = docData.getArea('Drawing') |
|
439 |
|
|
440 |
startPt = self.startPoint() |
|
441 |
endPt = self.endPoint() |
|
442 |
length = self.length() |
|
443 |
direction = [(endPt[0] - startPt[0]) / length, (endPt[1] - startPt[1]) / length] |
|
444 |
|
|
445 |
left = min(startPt[0], endPt[0]) |
|
446 |
top = min(startPt[1], endPt[1]) |
|
447 |
right = max(startPt[0], endPt[0]) |
|
448 |
bottom = max(startPt[1], endPt[1]) |
|
449 |
|
|
450 |
rect = None |
|
451 |
if self.isVertical(): |
|
452 |
rect = QRectF(left - 10, top, (right - left) + 20, (bottom - top)) |
|
453 |
else: |
|
454 |
rect = QRectF(left, top - 10, (right - left), (bottom - top) + 20) |
|
455 |
|
|
456 |
docData = AppDocData.instance() |
|
457 |
area = docData.getArea('Drawing') |
|
458 |
img = np.array(AppDocData.instance().getCurrentPidSource().getPyImageOnRect(rect)) |
|
459 |
|
|
460 |
imgLine = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)[1] |
|
461 |
# remove noise |
|
462 |
imgLine = cv2.bitwise_not(imgLine) |
|
463 |
imgLine = cv2.erode(imgLine, np.ones((10, 10), np.uint8)) |
|
464 |
|
|
465 |
image, contours, hierarchy = cv2.findContours(imgLine, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) |
|
466 |
if contours: |
|
467 |
contours = sorted(contours, key=cv2.contourArea, reverse=True) |
|
468 |
[x, y, w, h] = cv2.boundingRect(contours[0]) |
|
469 |
if w > 10 and w < 100 and h > 10 and h < 100: # check arrow mark size |
|
470 |
imgArrowMark = imgLine[y:y+h, x:x+w] |
|
471 |
|
|
472 |
# DEBUG - display flow arrow area |
|
473 |
''' |
|
474 |
item = QGraphicsBoundingBoxItem(rect.left() + x - 10, rect.top() + y - 10, w + 20, h + 20) |
|
475 |
item.isSymbol = True |
|
476 |
item.angle = 0 |
|
477 |
item.setPen(QPen(Qt.red, 1, Qt.SolidLine)) |
|
478 |
item.setBrush(QBrush(QColor(255,255,0,100))) |
|
479 |
self.scene().addItem(item) |
|
480 |
''' |
|
481 |
# up to here |
|
482 |
|
|
483 |
edges = cv2.Canny(imgArrowMark, 50, 150, apertureSize=3) |
|
484 |
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 10, minLineLength=10, maxLineGap=5) |
|
485 |
|
|
486 |
####### HoughLinesP |
|
487 |
if lines is not None: |
|
488 |
maxLength = None |
|
489 |
selected = None |
|
490 |
for line in lines: |
|
491 |
for x1, y1, x2, y2 in line: |
|
492 |
dx = x2 - x1 |
|
493 |
dy = y2 - y1 |
|
494 |
selected = line |
|
495 |
length = math.sqrt(dx*dx + dy*dy) |
|
496 |
if maxLength is None or length > maxLength: |
|
497 |
maxLength = length |
|
498 |
selected = line |
|
499 |
|
|
500 |
for x1, y1, x2, y2 in selected: |
|
501 |
dx = math.fabs(x2 - x1) |
|
502 |
dy = math.fabs(y2 - y1) |
|
503 |
length = math.sqrt(dx*dx + dy*dy) |
|
504 |
dx /= length |
|
505 |
dy /= length |
|
506 |
if (self.isVertical() and (dx < 0.001 or math.fabs(dx - 1) < 0.001)) or (self.isHorizontal() and (dx < 0.001 or math.fabs(dx - 1) < 0.001)): continue |
|
507 |
dist1 = self.distanceTo((rect.left() + x + x1, rect.top() + y + y1)) |
|
508 |
dist2 = self.distanceTo((rect.left() + x + x2, rect.top() + y + y2)) |
|
509 |
if dist1 > dist2: # point which's distance is longer would be start point |
|
510 |
_start = (rect.left() + x + x1, rect.top() + y + y1) |
|
511 |
_end = (rect.left() + x + x2, rect.top() + y + y2) |
|
512 |
else: |
|
513 |
_start = (rect.left() + x + x2, rect.top() + y + y2) |
|
514 |
_end = (rect.left() + x + x1, rect.top() + y + y1) |
|
515 |
|
|
516 |
# DEBUG display detected line |
|
517 |
''' |
|
518 |
poly = QGraphicsPolylineItem() |
|
519 |
poly._pol.append(QPointF(_start[0], _start[1])) |
|
520 |
poly._pol.append(QPointF(_end[0], _end[1])) |
|
521 |
poly.setPen(QPen(Qt.red, 2, Qt.SolidLine)) |
|
522 |
poly.buildItem() |
|
523 |
self.scene().addItem(poly) |
|
524 |
''' |
|
525 |
# up to here |
|
526 |
|
|
527 |
dist1 = Point(startPt[0], startPt[1]).distance(Point(_start[0], _start[1])) |
|
528 |
dist2 = Point(startPt[0], startPt[1]).distance(Point(_end[0], _end[1])) |
|
529 |
if dist1 > dist2: |
|
530 |
startPt,endPt = endPt,startPt |
|
531 |
direction[0],direction[1] = -direction[0],-direction[1] |
|
532 |
self.reverse() |
|
533 |
|
|
534 |
center = [(startPt[0]+endPt[0])*0.5, (startPt[1]+endPt[1])*0.5] |
|
535 |
arrow = QEngineeringFlowArrowItem(center, direction) |
|
536 |
arrow.buildItem() |
|
537 |
self.scene().addItem(arrow) |
|
538 |
|
|
539 |
x = round(rect.left() + x - 5) |
|
540 |
y = round(rect.top() + y - 5) |
|
541 |
self.flowMark = ([x, y, w + 10, h + 10], None) |
|
542 |
else: |
|
543 |
pass |
|
544 |
except Exception as ex: |
|
545 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
546 |
|
|
547 |
''' |
|
548 |
@breif insert symbol |
|
549 |
@author humkyung |
|
550 |
@date 2018.04.22 |
|
551 |
''' |
|
552 |
def insertSymbol(self, symbol, pos): |
|
553 |
import math |
|
554 |
from shapely.geometry import Point |
|
555 |
from shapely import affinity |
|
556 |
|
|
557 |
vec = self.perpendicular() |
|
558 |
line = [(pos.x() - vec[0]*20, pos.y() - vec[1]*20),(pos.x() + vec[0]*20, pos.y() + vec[1]*20)] |
|
559 |
origin = self.intersection(line) |
|
560 |
transform = QTransform() |
|
561 |
transform.translate(origin.x, origin.y) |
|
562 |
angle = self.angle() |
|
563 |
transform.rotateRadians(-angle) |
|
564 |
transform.translate(-symbol.origin[0], -symbol.origin[1]) |
|
565 |
symbol.setTransform(transform) |
|
566 |
if 2 == len(symbol.connPts): # 2 way component |
|
567 |
for i in range(len(symbol.connPts)): |
|
568 |
rotatedPt = affinity.rotate(Point(symbol.connPts[i][0] - symbol.origin[0], symbol.connPts[i][1] - symbol.origin[1]), -angle, Point(0, 0), use_radians=True) |
|
569 |
symbol.connPts[i] = (origin.x+rotatedPt.x, origin.y+rotatedPt.y) |
|
570 |
|
|
571 |
dx1 = symbol.connPts[0][0] - self.startPoint()[0] |
|
572 |
dy1 = symbol.connPts[0][1] - self.startPoint()[1] |
|
573 |
length1 = math.sqrt(dx1*dx1 + dy1*dy1) |
|
574 |
dx2 = symbol.connPts[1][0] - self.startPoint()[0] |
|
575 |
dy2 = symbol.connPts[1][1] - self.startPoint()[1] |
|
576 |
length2 = math.sqrt(dx2*dx2 + dy2*dy2) |
|
577 |
|
|
578 |
if length1 < length2: |
|
579 |
processLine = QEngineeringLineItem() |
|
580 |
processLine._pol.append(QPointF(symbol.connPts[1][0], symbol.connPts[1][1])) |
|
581 |
processLine._pol.append(QPointF(self.endPoint()[0], self.endPoint()[1])) |
|
582 |
processLine.buildItem() |
|
583 |
processLine.conns.append(symbol) |
|
584 |
processLine.conns.append(self.conns[1]) |
|
585 |
self.scene().addItem(processLine) |
|
586 |
|
|
587 |
self._pol.replace(self._pol.count() - 1, QPointF(symbol.connPts[0][0], symbol.connPts[0][1])) |
|
588 |
self.conns[1] = symbol |
|
589 |
|
|
590 |
symbol.conns = [] |
|
591 |
symbol.conns.append(self) |
|
592 |
symbol.conns.append(processLine) |
|
593 |
else: |
|
594 |
processLine = QEngineeringLineItem() |
|
595 |
processLine._pol.append(QPointF(symbol.connPts[0][0], symbol.connPts[0][1])) |
|
596 |
processLine._pol.append(QPointF(self.endPoint()[0], self.endPoint()[1])) |
|
597 |
processLine.buildItem() |
|
598 |
processLine.conns.append(symbol) |
|
599 |
processLine.conns.append(self.conns[1]) |
|
600 |
self.scene().addItem(processLine) |
|
601 |
|
|
602 |
self._pol.replace(self._pol.count() - 1, QPointF(symbol.connPts[1][0], symbol.connPts[1][1])) |
|
603 |
self.conns[1] = symbol |
|
604 |
|
|
605 |
symbol.conns = [] |
|
606 |
symbol.conns.append(processLine) |
|
607 |
symbol.conns.append(self) |
|
608 |
|
|
609 |
self.buildItem() |
|
610 |
self.update() |
|
611 |
|
|
612 |
symbol.loc = [origin.x - symbol.origin[0], origin.y - symbol.origin[1]] |
|
613 |
symbol.size = [symbol.boundingRect().width(), symbol.boundingRect().height()] |
|
614 |
self.scene().addItem(symbol) |
|
615 |
for connector in symbol.connectors: |
|
616 |
self.scene().addItem(connector) |
|
617 |
|
|
618 |
''' |
|
619 |
@brief remove symbol |
|
620 |
@author humkyung |
|
621 |
@date 2018.04.23 |
|
622 |
''' |
|
623 |
def removeSymbol(self, symbol): |
|
624 |
import math |
|
625 |
|
|
626 |
if 2 == len(symbol.conns): # 2-way component |
|
627 |
connected = symbol.conns[0] if symbol.conns[0] is not self else symbol.conns[1] |
|
628 |
|
|
629 |
pts = [] |
|
630 |
pts.append(self.startPoint()) |
|
631 |
pts.append(self.endPoint()) |
|
632 |
pts.append(connected.startPoint()) |
|
633 |
pts.append(connected.endPoint()) |
|
634 |
|
|
635 |
self.scene().removeItem(connected) |
|
636 |
|
|
637 |
start = None |
|
638 |
end = None |
|
639 |
maxDist = None |
|
640 |
for i in range(len(pts)): |
|
641 |
for j in range(i+1,len(pts)): |
|
642 |
dx = pts[i][0] - pts[j][0] |
|
643 |
dy = pts[i][1] - pts[j][1] |
|
644 |
dist = math.sqrt(dx*dx + dy*dy) |
|
645 |
if maxDist is None: |
|
646 |
maxDist = dist |
|
647 |
start = pts[i] |
|
648 |
end = pts[j] |
|
649 |
elif dist > maxDist: |
|
650 |
maxDist = dist |
|
651 |
start = pts[i] |
|
652 |
end = pts[j] |
|
653 |
|
|
654 |
if (pts[0] == end) or (pts[1] == start): start,end = end,start |
|
655 |
|
|
656 |
self._pol.clear() |
|
657 |
self._pol.append(QPointF(start[0], start[1])) |
|
658 |
self._pol.append(QPointF(end[0], end[1])) |
|
659 |
self.buildItem() |
|
660 |
self.update() |
|
661 |
|
|
662 |
''' |
|
663 |
@brief update line type |
|
664 |
@author humkyung |
|
665 |
@date 2018.07.05 |
|
666 |
''' |
|
667 |
def updateLineType(self): |
|
668 |
from QEngineeringInstrumentItem import QEngineeringInstrumentItem |
|
669 |
|
|
670 |
if len(self.conns) == 2: |
|
671 |
lines = [item for item in self.conns if item is not None and type(item) is QEngineeringLineItem] |
|
672 |
insts = [item for item in self.conns if item is not None and type(item) is QEngineeringInstrumentItem] |
|
673 |
|
|
674 |
matches = [inst for inst in insts if (inst.measuredVariableCode + inst.typeModifier) not in ['FT', 'PT', 'TT', 'TI', 'TG', 'PG']] |
|
675 |
if matches: |
|
676 |
self.lineType = 'Electric' |
|
677 |
|
|
678 |
pool = [item for item in self.conns if item is not None and type(item) is QEngineeringLineItem] |
|
679 |
visited = [] |
|
680 |
visited.extend(pool) |
|
681 |
while len(pool): |
|
682 |
line = pool.pop() |
|
683 |
line.lineType = 'Electric' |
|
684 |
|
|
685 |
matches = [item for item in line.conns if item is not None and item not in visited and type(item) is QEngineeringLineItem] |
|
686 |
pool.extend(matches) |
|
687 |
visited.extend(matches) |
|
688 |
else: |
|
689 |
matches = [inst for inst in insts if (inst.measuredVariableCode + inst.typeModifier) in ['FT', 'PT', 'TT', 'TI', 'TG', 'PG']] |
|
690 |
if matches: |
|
691 |
self.lineType = 'Connect To Process' |
|
692 |
|
|
693 |
def hoverEnterEvent(self, event): |
|
694 |
pass |
|
695 |
|
|
696 |
def hoverLeaveEvent(self, event): |
|
697 |
pass |
|
698 |
|
|
699 |
def hoverMoveEvent(self, event): |
|
700 |
pass |
|
701 |
|
|
702 |
''' |
|
703 |
@brief remove item when user press delete key |
|
704 |
@author humkyung |
|
705 |
@date 2018.04.23 |
|
706 |
''' |
|
707 |
def keyPressEvent(self, event): |
|
708 |
if event.key() == Qt.Key_Delete: |
|
709 |
#self.removed.emit((QGraphicsPathItem)(self)) |
|
710 |
self.scene().removeItem(self) |
|
711 |
|
|
712 |
''' |
|
713 |
@brief draw rect when item is selected |
|
714 |
@author humkyung |
|
715 |
@date 2018.07.07 |
|
716 |
''' |
|
717 |
def drawFocusRect(self, painter): |
|
718 |
self.focuspen = QPen(Qt.DotLine) |
|
719 |
self.focuspen.setColor(Qt.black) |
|
720 |
self.focuspen.setWidthF(1.5) |
|
721 |
hilightColor = QColor(255, 0, 0, 127) |
|
722 |
painter.setBrush(QBrush(hilightColor)) |
|
723 |
painter.setPen(self.focuspen) |
|
724 |
painter.drawRect(self.boundingRect()) |
|
725 |
|
|
726 |
''' |
|
727 |
@brief override paint method |
|
728 |
''' |
|
729 |
def paint(self, painter, option, widget): |
|
730 |
QGraphicsPolylineItem.paint(self, painter, option, widget) |
|
731 |
if self.isSelected(): |
|
732 |
self.drawFocusRect(painter) |
|
733 |
|
|
734 |
''' |
|
735 |
@brief draw self to given image |
|
736 |
@author humkyung |
|
737 |
@date 2018.06.21 |
|
738 |
''' |
|
739 |
def drawToImage(self, img, color, thickness): |
|
740 |
try: |
|
741 |
# write recognized lines to image |
|
742 |
ptStart = self.startPoint() |
|
743 |
ptEnd = self.endPoint() |
|
744 |
cv2.line(img, (round(ptStart[0]), round(ptStart[1])), (round(ptEnd[0]), round(ptEnd[1])), color, thickness) |
|
745 |
# up to here |
|
746 |
|
|
747 |
if self.flowMark is not None: |
|
748 |
x, y, w, h = self.flowMark[0] |
|
749 |
img[y:(y+h), x:(x+w)] = color |
|
750 |
except Exception as ex: |
|
751 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
752 |
|
|
753 |
''' |
|
754 |
@brief parse xml code |
|
755 |
@author humkyung |
|
756 |
@date 2018.06.27 |
|
757 |
''' |
|
758 |
@staticmethod |
|
759 |
def fromXml(node): |
|
760 |
item = None |
|
761 |
try: |
|
762 |
startPoint = [float(x) for x in node.find('STARTPOINT').text.split(',')] |
|
763 |
endPoint = [float(x) for x in node.find('ENDPOINT').text.split(',')] |
|
764 |
lineType = node.find('TYPE').text if node.find('TYPE') else 'Primary' |
|
765 |
item = QEngineeringLineItem(vertices=[startPoint, endPoint]) |
|
766 |
item.lineType = lineType |
|
767 |
item.buildItem() |
|
768 |
except Exception as ex: |
|
769 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
770 |
|
|
771 |
return item |
|
772 |
|
|
773 |
''' |
|
774 |
@brief generate xml code |
|
775 |
@author humkyung |
|
776 |
@date 2018.04.23 |
|
777 |
@history humkyung 2018.06.27 write line type to xml |
|
778 |
''' |
|
779 |
def toXml(self): |
|
780 |
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree |
|
781 |
|
|
782 |
try: |
|
783 |
node = Element('LINE') |
|
784 |
uidNode = Element('UID') |
|
785 |
uidNode.text = str(self.uid) |
|
786 |
node.append(uidNode) |
|
787 |
|
|
788 |
startPt = self.startPoint() |
|
789 |
endPt = self.endPoint() |
|
790 |
|
|
791 |
startNode = Element('STARTPOINT') |
|
792 |
startNode.text = '{},{}'.format(startPt[0], startPt[1]) |
|
793 |
node.append(startNode) |
|
794 |
|
|
795 |
endNode = Element('ENDPOINT') |
|
796 |
endNode.text = '{},{}'.format(endPt[0], endPt[1]) |
|
797 |
node.append(endNode) |
|
798 |
|
|
799 |
typeNode = Element('TYPE') |
|
800 |
typeNode.text = self.lineType |
|
801 |
node.append(typeNode) |
|
802 |
except Exception as ex: |
|
803 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
804 |
|
|
805 |
return node |
|
806 |
|
|
807 |
''' |
|
808 |
@brief Delete Line Item from scene |
|
809 |
@author Jeongwoo |
|
810 |
@date 2018.05.29 |
|
811 |
@history 2018.05.29 Add parameter 'self' / Make comments emit() |
|
812 |
''' |
|
813 |
def deleteLineItemFromScene(self): |
|
814 |
#self.removed.emit(self) |
|
815 |
self.scene().removeItem(self) |
|
816 |
|
|
817 |
''' |
|
818 |
@brief Add Line Item |
|
819 |
@author Jeongwoo |
|
820 |
@date 2018.05.29 |
|
821 |
''' |
|
822 |
def addLineItemToScene(self, scene): |
|
823 |
scene.addItem(self) |
DTI_PID/DTI_PID/Shapes/GraphicsPolylineItem.py | ||
---|---|---|
1 |
import sys |
|
2 |
import os.path |
|
3 |
import copy |
|
4 |
try: |
|
5 |
from PyQt5.QtCore import Qt, QRectF, pyqtSignal, QObject, QT_VERSION_STR |
|
6 |
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QBrush, QPen, QPolygonF, QColor |
|
7 |
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QGraphicsItem, QAbstractGraphicsShapeItem, QGraphicsPathItem |
|
8 |
except ImportError: |
|
9 |
try: |
|
10 |
from PyQt4.QtCore import Qt, QRectF, QObject, pyqtSignal, QT_VERSION_STR |
|
11 |
from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QColor |
|
12 |
except ImportError: |
|
13 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
|
14 |
from QEngineeringAbstractItem import QEngineeringAbstractItem |
|
15 |
|
|
16 |
class QGraphicsPolylineItem(QGraphicsPathItem, QEngineeringAbstractItem): |
|
17 |
onRemoved = pyqtSignal(QGraphicsItem) |
|
18 |
|
|
19 |
''' |
|
20 |
@history 2018.05.11 Jeongwoo Declare variable self.pen |
|
21 |
2018.05.15 Jeongwoo Change method to call parent's __init__ |
|
22 |
2018.05.25 Jeongwoo Change self.pen's default color (red → blue) |
|
23 |
''' |
|
24 |
def __init__(self, uid=None, parent=None): |
|
25 |
import uuid |
|
26 |
|
|
27 |
try: |
|
28 |
QGraphicsPathItem.__init__(self, parent) |
|
29 |
QEngineeringAbstractItem.__init__(self) |
|
30 |
self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable) |
|
31 |
|
|
32 |
self.uid = uuid.uuid4() if uid is None else uid |
|
33 |
self._vertices = [] |
|
34 |
self.isCreated = False |
|
35 |
self._pt = None |
|
36 |
self._pol = QPolygonF() |
|
37 |
self._path = None |
|
38 |
self.setPen(QPen(Qt.blue, 4, Qt.SolidLine)) # set default pen |
|
39 |
|
|
40 |
self.transfer = Transfer() |
|
41 |
except Exception as ex: |
|
42 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
43 |
|
|
44 |
''' |
|
45 |
@brief construct a polyline |
|
46 |
''' |
|
47 |
def process(self, param): |
|
48 |
if ('mousePressEvent' == param[0]) and (param[1].button() == Qt.LeftButton): |
|
49 |
self._vertices.append(param[2]) |
|
50 |
elif ('mouseMoveEvent' == param[0]): |
|
51 |
self._pt = param[2] |
|
52 |
elif ('mouseReleaseEvent' == param[0]) and (param[1].button() == Qt.RightButton): |
|
53 |
self.isCreated = True |
|
54 |
|
|
55 |
''' |
|
56 |
@brief clone an object |
|
57 |
''' |
|
58 |
def clone(self): |
|
59 |
clone = QGraphicsPolylineItem() |
|
60 |
clone._vertices = copy.deepcopy(self._vertices) |
|
61 |
clone.isCreated = self.isCreated |
|
62 |
|
|
63 |
return clone |
|
64 |
|
|
65 |
def init(self): |
|
66 |
self._vertices = [] |
|
67 |
self._pol.clear() |
|
68 |
self._pt = None |
|
69 |
self.isCreated = False |
|
70 |
|
|
71 |
''' |
|
72 |
@build build path |
|
73 |
@author humkyung |
|
74 |
@date 2018.04.23 |
|
75 |
''' |
|
76 |
def buildItem(self): |
|
77 |
self._path = QPainterPath() |
|
78 |
self._path.addPolygon(self._pol) |
|
79 |
self.setPath(self._path) |
|
80 |
self.isCreated = True |
|
81 |
|
|
82 |
''' |
|
83 |
@brief return start point |
|
84 |
@author humkyung |
|
85 |
@date 2018.04.16 |
|
86 |
''' |
|
87 |
def startPoint(self): |
|
88 |
at = self._pol.at(0) |
|
89 |
return (at.x(), at.y()) |
|
90 |
|
|
91 |
''' |
|
92 |
@brief return last point |
|
93 |
@author humkyung |
|
94 |
@date 2018.04.16 |
|
95 |
''' |
|
96 |
def endPoint(self): |
|
97 |
at = self._pol.at(self._pol.count() - 1) |
|
98 |
return (at.x(), at.y()) |
|
99 |
|
|
100 |
''' |
|
101 |
@brief return bouding rectangle of polyline |
|
102 |
@author humkyung |
|
103 |
@date 2018.07.23 |
|
104 |
''' |
|
105 |
def boundingRect(self): |
|
106 |
rect = QRectF() |
|
107 |
|
|
108 |
if self._path is not None: |
|
109 |
rect = self._path.boundingRect() |
|
110 |
rect = QRectF(rect.left() - 5, rect.top() - 5, rect.width() + 10, rect.height() + 10) |
|
111 |
else: |
|
112 |
minX = None |
|
113 |
minY = None |
|
114 |
maxX = None |
|
115 |
maxY = None |
|
116 |
for pt in self._vertices: |
|
117 |
if minX is None or pt[0] < minX: |
|
118 |
minX = pt[0] |
|
119 |
if minY is None or pt[1] < minY: |
|
120 |
minY = pt[1] |
|
121 |
|
|
122 |
if maxX is None or pt[0] > maxX: |
|
123 |
maxX = pt[0] |
|
124 |
if maxY is None or pt[1] > maxY: |
|
125 |
maxY = pt[1] |
|
126 |
|
|
127 |
if self._pt is not None: |
|
128 |
if minX is None or self._pt[0] < minX: |
|
129 |
minX = self._pt[0] |
|
130 |
if minY is None or self._pt[1] < minY: |
|
131 |
minY = self._pt[1] |
|
132 |
|
|
133 |
if maxX is None or self._pt[0] > maxX: |
|
134 |
maxX = self._pt[0] |
|
135 |
if maxY is None or self._pt[1] > maxY: |
내보내기 Unified diff