hytos / DTI_PID / DTI_PID / QtImageViewer.py @ dc05dcb9
이력 | 보기 | 이력해설 | 다운로드 (23.4 KB)
1 |
# coding: utf-8
|
---|---|
2 |
import sys |
3 |
import os.path |
4 |
try:
|
5 |
from PyQt5.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR, QPointF |
6 |
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QPainter, QCursor, QPen, QBrush, QColor, QTransform |
7 |
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QApplication |
8 |
except ImportError: |
9 |
try:
|
10 |
from PyQt4.QtCore import * |
11 |
from PyQt4.QtGui import * |
12 |
except ImportError: |
13 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
14 |
|
15 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands') |
16 |
import DefaultCommand |
17 | |
18 |
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes') |
19 |
from EngineeringLineItem import QEngineeringLineItem |
20 |
from SymbolSvgItem import SymbolSvgItem |
21 | |
22 |
__author__ = "Marcel Goldschen-Ohm <marcel.goldschen@gmail.com>"
|
23 |
__version__ = '0.9.0'
|
24 | |
25 | |
26 |
class QtImageViewer(QGraphicsView): |
27 |
""" PyQt image viewer widget for a QPixmap in a QGraphicsView scene with mouse zooming and panning.
|
28 |
Displays a QImage or QPixmap (QImage is internally converted to a QPixmap).
|
29 |
To display any other image format, you must first convert it to a QImage or QPixmap.
|
30 |
Some useful image format conversion utilities:
|
31 |
qimage2ndarray: NumPy ndarray <==> QImage (https://github.com/hmeine/qimage2ndarray)
|
32 |
ImageQt: PIL Image <==> QImage (https://github.com/python-pillow/Pillow/blob/master/PIL/ImageQt.py)
|
33 |
Mouse interaction:
|
34 |
Left mouse button drag: Pan image.
|
35 |
Right mouse button drag: Zoom box.
|
36 |
Right mouse button doubleclick: Zoom to show entire image.
|
37 |
"""
|
38 | |
39 |
# Mouse button signals emit image scene (x, y) coordinates.
|
40 |
# !!! For image (row, column) matrix indexing, row = y and column = x.
|
41 |
leftMouseButtonPressed = pyqtSignal(float, float) |
42 |
rightMouseButtonPressed = pyqtSignal(float, float) |
43 |
leftMouseButtonMoved = pyqtSignal(float, float) |
44 |
rightMouseButtonMoved = pyqtSignal(float, float) |
45 |
leftMouseButtonReleased = pyqtSignal(float, float) |
46 |
rightMouseButtonReleased = pyqtSignal(float, float) |
47 |
leftMouseButtonDoubleClicked = pyqtSignal(float, float) |
48 |
rightMouseButtonDoubleClicked = pyqtSignal(float, float) |
49 |
#itemRemoved = pyqtSignal(QGraphicsItem)
|
50 |
startPointChanged = pyqtSignal(float, float) |
51 | |
52 |
'''
|
53 |
@history 2018.06.27 Jeongwoo Change zoom rule (Qt.KeepAspectRatioByExpanding → Qt.KeepAspectRatio)
|
54 |
'''
|
55 |
def __init__(self, mainWindow = None): |
56 |
QGraphicsView.__init__(self)
|
57 | |
58 |
self.mainWindow = mainWindow
|
59 |
# Image is displayed as a QPixmap in a QGraphicsScene attached to this QGraphicsView.
|
60 |
self.command = None |
61 |
self.scene = QGraphicsScene(self) |
62 |
self.setScene(self.scene) |
63 |
self.scene.setBackgroundBrush(Qt.gray)
|
64 |
self.crosshairPos = None |
65 | |
66 |
self.isPressCtrl = False |
67 |
self.scaleFactor = 1.0 |
68 |
self.numScheduledScalings = 0 |
69 |
self.isOriginalPointSelected = False |
70 | |
71 |
# Store a local handle to the scene's current image pixmap.
|
72 |
self._pixmapHandle = None |
73 | |
74 |
# Image aspect ratio mode.
|
75 |
# !!! ONLY applies to full image. Aspect ratio is always ignored when zooming.
|
76 |
# Qt.IgnoreAspectRatio: Scale image to fit viewport.
|
77 |
# Qt.KeepAspectRatio: Scale image to fit inside viewport, preserving aspect ratio.
|
78 |
# Qt.KeepAspectRatioByExpanding: Scale image to fill the viewport, preserving aspect ratio.
|
79 |
self.aspectRatioMode = Qt.KeepAspectRatio
|
80 | |
81 |
# Scroll bar behaviour.
|
82 |
# Qt.ScrollBarAlwaysOff: Never shows a scroll bar.
|
83 |
# Qt.ScrollBarAlwaysOn: Always shows a scroll bar.
|
84 |
# Qt.ScrollBarAsNeeded: Shows a scroll bar only when zoomed.
|
85 |
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
86 |
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
87 | |
88 |
# Stack of QRectF zoom boxes in scene coordinates.
|
89 |
self.zoomStack = []
|
90 | |
91 |
self.setRenderHint(QPainter.Antialiasing)
|
92 | |
93 |
self.setAcceptDrops(True) # enable drop |
94 | |
95 |
# Flags for enabling/disabling mouse interaction.
|
96 |
self.canZoom = True |
97 |
self.canPan = True |
98 |
self.setMouseTracking(True) |
99 |
self.command = None |
100 | |
101 |
# set currentAttribute
|
102 |
self.currentAttribute = '' |
103 | |
104 |
'''
|
105 |
@brief Return Pixmap Handler
|
106 |
@author Jeongwoo
|
107 |
@date 2018.06.11
|
108 |
'''
|
109 |
def getPixmapHandle(self): |
110 |
return self._pixmapHandle |
111 | |
112 |
'''
|
113 |
@brief Use Default ImageViewer Command
|
114 |
@author Jeongwoo
|
115 |
@date 18.04.10
|
116 |
@history .
|
117 |
'''
|
118 |
def useDefaultCommand(self): |
119 |
""" Use Default Command
|
120 |
"""
|
121 |
self.command = DefaultCommand.DefaultCommand(self) |
122 | |
123 |
def hasImage(self): |
124 |
""" Returns whether or not the scene contains an image pixmap.
|
125 |
"""
|
126 |
return self._pixmapHandle is not None |
127 | |
128 |
def clearImage(self): |
129 |
""" Removes the current image pixmap from the scene if it exists.
|
130 |
"""
|
131 |
if self.hasImage(): |
132 |
self.scene.removeItem(self._pixmapHandle) |
133 |
self._pixmapHandle = None |
134 | |
135 |
def pixmap(self): |
136 |
""" Returns the scene's current image pixmap as a QPixmap, or else None if no image exists.
|
137 |
:rtype: QPixmap | None
|
138 |
"""
|
139 |
if self.hasImage(): |
140 |
return self._pixmapHandle.pixmap() |
141 |
return None |
142 | |
143 |
def image(self): |
144 |
""" Returns the scene's current image pixmap as a QImage, or else None if no image exists.
|
145 |
:rtype: QImage | None
|
146 |
"""
|
147 |
if self.hasImage(): |
148 |
return self._pixmapHandle.pixmap().toImage() |
149 |
return None |
150 | |
151 |
def setImage(self, image): |
152 |
""" Set the scene's current image pixmap to the input QImage or QPixmap.
|
153 |
Raises a RuntimeError if the input image has type other than QImage or QPixmap.
|
154 |
:type image: QImage | QPixmap
|
155 |
"""
|
156 |
try:
|
157 |
if type(image) is QPixmap: |
158 |
pixmap = image |
159 |
elif type(image) is QImage: |
160 |
pixmap = QPixmap.fromImage(image) |
161 |
else:
|
162 |
raise RuntimeError("ImageViewer.setImage: Argument must be a QImage or QPixmap.") |
163 | |
164 |
self.clearImage()
|
165 |
self.scene.clear()
|
166 | |
167 |
if self.hasImage(): |
168 |
self._pixmapHandle.setPixmap(pixmap)
|
169 |
else:
|
170 |
self._pixmapHandle = self.scene.addPixmap(pixmap) |
171 | |
172 |
self.setSceneRect(QRectF(pixmap.rect())) # Set scene size to image size. |
173 |
self.updateViewer()
|
174 |
except Exception as ex: |
175 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
176 | |
177 |
'''
|
178 |
@brief open a image file selected by user
|
179 |
@author
|
180 |
@date
|
181 |
'''
|
182 |
def loadImageFromFile(self, folder='', fileName=""): |
183 |
import cv2 |
184 | |
185 |
""" Load an image from file.
|
186 |
Without any arguments, loadImageFromFile() will popup a file dialog to choose the image file.
|
187 |
With a fileName argument, loadImageFromFile(fileName) will attempt to load the specified image file directly.
|
188 |
"""
|
189 |
try:
|
190 |
if len(fileName) == 0: |
191 |
options = QFileDialog.Options() |
192 |
options |= QFileDialog.DontUseNativeDialog |
193 |
if QT_VERSION_STR[0] == '4': |
194 |
fileName = QFileDialog.getOpenFileName(self, "Open image file", os.getcwd() if folder == '' else folder, "Image files(*.png *.jpg)", options=options) |
195 |
elif QT_VERSION_STR[0] == '5': |
196 |
fileName, dummy = QFileDialog.getOpenFileName(self, "Open image file", os.getcwd() if folder == '' else folder, "Image files(*.png *.jpg)", options=options) |
197 |
if len(fileName) and os.path.isfile(fileName): |
198 |
cvImg = cv2.cvtColor(cv2.imread(fileName), cv2.COLOR_BGR2GRAY) |
199 |
blur = cv2.GaussianBlur(cvImg, (5,5),0) |
200 |
cvImg = cv2.threshold(cvImg, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1] |
201 |
bytesPerLine = cvImg.shape[1]
|
202 |
image = QImage(cvImg.data, cvImg.shape[1], cvImg.shape[0], bytesPerLine, QImage.Format_Indexed8) |
203 |
self.setImage(image)
|
204 |
except Exception as ex: |
205 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
206 | |
207 |
return fileName
|
208 | |
209 |
'''
|
210 |
@history 2018.06.27 Jeongwoo Change zoom rule (Qt.KeepAspectRatioByExpanding → Qt.KeepAspectRatio)
|
211 |
'''
|
212 |
def updateViewer(self): |
213 |
""" Show current zoom (if showing entire image, apply current aspect ratio mode).
|
214 |
"""
|
215 |
if not self.hasImage(): |
216 |
return
|
217 |
if len(self.zoomStack):# and self.sceneRect().contains(self.zoomStack[-1]): |
218 |
self.fitInView(self.zoomStack[-1], Qt.KeepAspectRatio) # Show zoomed rect (ignore aspect ratio). |
219 |
else:
|
220 |
self.zoomStack = [] # Clear the zoom stack (in case we got here because of an invalid zoom). |
221 |
self.fitInView(self.sceneRect(), self.aspectRatioMode) # Show entire image (use current aspect ratio mode). |
222 | |
223 |
def zoomImageInit(self): |
224 |
if self.hasImage(): |
225 |
self.zoomStack = []
|
226 |
self.updateViewer()
|
227 |
self.setCursor(QCursor(Qt.ArrowCursor))
|
228 | |
229 |
'''
|
230 |
@brief Zoom in & out image
|
231 |
@author Jeongwoo
|
232 |
@date -
|
233 |
@history 18.04.11 Jeongwoo add parameter 'adjust' (@ref ResultTreeWidget.itemClickEvent(self, item, columnNo))
|
234 |
'''
|
235 |
def zoomImage(self, isZoomIn, event, adjust = 1): |
236 |
""" Zoom in & out
|
237 |
"""
|
238 |
HALF_SIZE = 300
|
239 |
clickPos = event.pos() |
240 |
scenePos1 = self.mapToScene(clickPos.x() - HALF_SIZE//adjust, clickPos.y() - HALF_SIZE//adjust)
|
241 |
scenePos2 = self.mapToScene(clickPos.x() + HALF_SIZE//adjust, clickPos.y() + HALF_SIZE//adjust)
|
242 |
if isZoomIn:
|
243 |
zoomArea = QRectF(QPointF(scenePos1.x() if scenePos1.x() > 0 else 0, scenePos1.y() if scenePos1.y() > 0 else 0), QPointF(scenePos2.x(), scenePos2.y())) |
244 |
#self.fitInView(zoomArea, Qt.KeepAspectRatioByExpanding)
|
245 |
viewBBox = self.zoomStack[-1] if len(self.zoomStack) else self.sceneRect() |
246 |
selectionBBox = zoomArea.intersected(viewBBox) |
247 |
self.scene.setSelectionArea(QPainterPath()) # Clear current selection area. |
248 |
if selectionBBox.width() > HALF_SIZE*2 and selectionBBox.height() > HALF_SIZE*2: |
249 |
if selectionBBox.isValid() and (selectionBBox != viewBBox): |
250 |
self.zoomStack.append(selectionBBox)
|
251 |
self.updateViewer()
|
252 |
else:
|
253 |
self.scene.setSelectionArea(QPainterPath()) # Clear current selection area. |
254 |
if len(self.zoomStack): |
255 |
self.zoomStack.pop()
|
256 |
self.updateViewer()
|
257 | |
258 |
def resizeEvent(self, event): |
259 |
""" Maintain current zoom on resize.
|
260 |
"""
|
261 |
self.updateViewer()
|
262 | |
263 |
'''
|
264 |
@brief mouse move event
|
265 |
'''
|
266 |
def mouseMoveEvent(self, event): |
267 |
try:
|
268 |
if self.command is not None: |
269 |
scenePos = self.mapToScene(event.pos())
|
270 |
self.command.execute(['mouseMoveEvent', event, scenePos]) |
271 |
if self.command.isTreated == True: return |
272 |
except Exception as ex: |
273 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
274 | |
275 |
QGraphicsView.mouseMoveEvent(self, event)
|
276 | |
277 |
'''
|
278 |
@brief
|
279 |
@author
|
280 |
@date
|
281 |
@history block clear selection when right mouse button is clicked
|
282 |
'''
|
283 |
def mousePressEvent(self, event): |
284 |
try:
|
285 |
if self.command is not None: |
286 |
scenePos = self.mapToScene(event.pos())
|
287 |
self.command.execute(['mousePressEvent', event, scenePos]) |
288 |
if self.command.isTreated == True: return |
289 |
except Exception as ex: |
290 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
291 | |
292 |
if event.button() != Qt.RightButton:
|
293 |
QGraphicsView.mousePressEvent(self, event)
|
294 | |
295 |
def mouseReleaseEvent(self, event): |
296 |
""" Stop mouse pan or zoom mode (apply zoom if valid).
|
297 |
"""
|
298 | |
299 |
try:
|
300 |
if self.command is not None: |
301 |
scenePos = self.mapToScene(event.pos())
|
302 |
instance = self.command.execute(['mouseReleaseEvent', event, scenePos]) |
303 |
if instance is not None: |
304 |
self.scene.addItem(instance)
|
305 | |
306 |
if self.command is not None and self.command.isTreated == True: |
307 |
# SelectAttribute 경우만 추가
|
308 |
if self.command.name == "SelectAttribute": |
309 |
items = self.scene.selectedItems()
|
310 |
if self.command.resultItem is not None and items is not None and len(items) == 1: |
311 |
attrItem = self.command.resultItem
|
312 |
self.command.resultItem.attribute = self.currentAttribute |
313 |
# 기존 연결되있는 다른 SymbolItem의 Attr 제거
|
314 |
items[0].removeAttr(attrItem)
|
315 |
# add attr
|
316 |
items[0].attrs.append(attrItem)
|
317 |
#resultPropertyTable refresh
|
318 |
self.mainWindow.refreshResultPropertyTableWidget()
|
319 |
elif self.command.name == 'Default' and self.command.isCopy: |
320 |
return
|
321 |
self.command = DefaultCommand.DefaultCommand(self) |
322 |
cursor = QCursor(Qt.ArrowCursor) |
323 |
QApplication.instance().setOverrideCursor(cursor) |
324 |
return
|
325 |
except Exception as ex: |
326 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
327 | |
328 |
QGraphicsView.mouseReleaseEvent(self, event)
|
329 |
#self.mainWindow.refreshResultPropertyTableWidget()
|
330 | |
331 |
def mouseDoubleClickEvent(self, event): |
332 |
""" Show entire image.
|
333 |
"""
|
334 | |
335 |
scenePos = self.mapToScene(event.pos())
|
336 |
if self.command is not None: |
337 |
instance = self.command.execute(['mouseDoubleClickEvent', event, scenePos]) |
338 |
if self.command.isTreated == True: return |
339 | |
340 |
if event.button() == Qt.LeftButton:
|
341 |
self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
|
342 |
elif event.button() == Qt.RightButton:
|
343 |
if self.canZoom: |
344 |
self.zoomStack = [] # Clear zoom stack. |
345 |
self.updateViewer()
|
346 |
self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
|
347 |
|
348 |
QGraphicsView.mouseDoubleClickEvent(self, event)
|
349 | |
350 |
'''
|
351 |
@brief key press event
|
352 |
@author Jeongwoo
|
353 |
@date 2018.??.??
|
354 |
@history send escape key event to command
|
355 |
'''
|
356 |
def keyPressEvent(self, event): |
357 |
try:
|
358 |
if event.key() == Qt.Key_Control:
|
359 |
self.isPressCtrl = True |
360 |
elif event.key() == Qt.Key_Delete:
|
361 |
for item in self.scene.selectedItems(): |
362 |
item.transfer.onRemoved.emit(item) |
363 |
elif event.key() == Qt.Key_Escape:
|
364 |
if self.command is not None: |
365 |
self.command.execute(['keyPressEvent', event, []]) |
366 |
else:
|
367 |
if self.command is not None: |
368 |
self.command.execute(['keyPressEvent', event, []]) |
369 | |
370 |
QGraphicsView.keyPressEvent(self, event)
|
371 |
except Exception as ex: |
372 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
373 | |
374 |
'''
|
375 |
@brief key release event
|
376 |
@author Jeongwoo
|
377 |
@date 2018.??.??
|
378 |
'''
|
379 |
def keyReleaseEvent(self, event): |
380 |
if event.key() == Qt.Key_Control:
|
381 |
self.isPressCtrl = False |
382 |
elif event.key() == Qt.Key_Delete:
|
383 |
pass
|
384 | |
385 |
QGraphicsView.keyReleaseEvent(self, event)
|
386 | |
387 |
def wheelEvent(self, event): |
388 |
if self.isPressCtrl == True: |
389 |
if self.canZoom and self.hasImage(): |
390 |
numDegrees = event.angleDelta() / 8
|
391 |
if numDegrees is not None: |
392 |
if numDegrees.y() > 0: |
393 |
self.zoomImage(True, event) |
394 |
elif numDegrees.y() < 0: |
395 |
self.zoomImage(False, event) |
396 |
#print("Zoomable")
|
397 |
#numDegrees = event.angleDelta().y() // 8
|
398 |
#numSteps = numDegrees // 15
|
399 |
#self.numScheduledScalings = self.numScheduledScalings + numSteps
|
400 |
#if self.numScheduledScalings * numSteps < 0:
|
401 |
# self.numScheduledScalings = numSteps
|
402 |
#self.scaleFactor = 1.0 + (self.numScheduledScalings / 300.0)
|
403 |
#print("scaleFactor : " + str(self.scaleFactor))
|
404 |
#self.scale(self.scaleFactor, self.scaleFactor)
|
405 |
else:
|
406 |
super().wheelEvent(event)
|
407 | |
408 |
def drawForeground(self, painter, rect): |
409 |
image = self.image()
|
410 |
if image is not None: |
411 |
width = image.width() |
412 |
height = image.height() |
413 |
if self.crosshairPos is not None: |
414 |
pen = QPen() |
415 |
pen.setColor(QColor(180, 180, 180)) |
416 |
pen.setStyle(Qt.DashLine) |
417 |
pen.setWidthF(0.3)
|
418 |
painter.setPen(pen) |
419 |
painter.drawLine(self.crosshairPos.x(), 0, self.crosshairPos.x(), height)#Vertical |
420 |
painter.drawLine(0, self.crosshairPos.y(), width, self.crosshairPos.y())#Horizontal |
421 |
#else:
|
422 |
# painter.eraseRect(QRectF(0, 0, width, height))
|
423 | |
424 |
'''
|
425 |
@brief draw background
|
426 |
@author humkyung
|
427 |
@date 2018.07.23
|
428 |
'''
|
429 |
def drawBackground(self, painter, rect): |
430 |
QGraphicsView.drawBackground(self, painter, rect)
|
431 |
'''
|
432 |
gridSize = max(int(rect.width() / 100), int(rect.height() / 100))
|
433 |
left = int(rect.left()) - (int(rect.left()) % gridSize)
|
434 |
top = int(rect.top()) - (int(rect.top()) % gridSize)
|
435 |
|
436 |
pen = QPen()
|
437 |
pen.setColor(Qt.black)
|
438 |
pen.setWidthF(1.5)
|
439 |
painter.setPen(pen)
|
440 |
|
441 |
points = []
|
442 |
for x in range(left, int(rect.right()), gridSize):
|
443 |
for y in range(top, int(rect.bottom()), gridSize):
|
444 |
painter.drawPoint(QPointF(x, y))
|
445 |
'''
|
446 | |
447 |
'''
|
448 |
@history 2018.06.11 Jeongwoo Change method to manage guideline items
|
449 |
'''
|
450 |
GUIDELINE_ITEMS = [] |
451 |
def showGuideline(self, isShow): |
452 |
image = self.image()
|
453 |
width = image.width() |
454 |
height = image.height() |
455 |
pen = QPen() |
456 |
pen.setColor(QColor(180, 180, 180)) |
457 |
pen.setStyle(Qt.DashLine) |
458 |
pen.setWidthF(0.5)
|
459 |
if isShow:
|
460 |
verticalLine = self.scene.addLine(width/2, 0, width/2, height, pen) |
461 |
horizontalLine = self.scene.addLine(0, height/2, width, height/2, pen) |
462 |
self.GUIDELINE_ITEMS.clear()
|
463 |
self.GUIDELINE_ITEMS.append(verticalLine)
|
464 |
self.GUIDELINE_ITEMS.append(horizontalLine)
|
465 |
else:
|
466 |
items = self.scene.items()
|
467 |
for item in self.GUIDELINE_ITEMS: |
468 |
if item in items: |
469 |
self.scene.removeItem(item)
|
470 |
self.GUIDELINE_ITEMS.clear()
|
471 | |
472 |
'''
|
473 |
@brief drag enter event
|
474 |
@author humkyung
|
475 |
@date 2018.04.17
|
476 |
'''
|
477 |
def dragEnterEvent(self, event): |
478 |
event.acceptProposedAction() |
479 | |
480 |
'''
|
481 |
@brief drag move event
|
482 |
@author humkyung
|
483 |
@date 2018.04.17
|
484 |
'''
|
485 |
def dragMoveEvent(self, event): |
486 |
event.acceptProposedAction() |
487 | |
488 |
'''
|
489 |
@brief drop event
|
490 |
@author humkyung
|
491 |
@date 2018.04.17
|
492 |
@history 2018.06.08 Jeongwoo Add Paramter on SymbolSvgItem.buildItem()
|
493 |
'''
|
494 |
def dropEvent(self, event): |
495 |
from AppDocData import AppDocData |
496 |
import symbol |
497 |
|
498 |
scenePos = self.mapToScene(event.pos())
|
499 |
svgFileName = event.mimeData().text() |
500 |
svg = self.createSymbolObject(svgFileName)
|
501 |
self.matchSymbolToLine(svg, scenePos)
|
502 |
event.acceptProposedAction() |
503 | |
504 |
'''
|
505 |
@brief drop create Symbol
|
506 |
@author kyouho
|
507 |
@date 2018.07.27
|
508 |
'''
|
509 |
def createSymbolObject(self, svgFileName): |
510 |
from AppDocData import AppDocData |
511 |
import symbol |
512 |
|
513 |
symbol = AppDocData.instance().getSymbolByQuery('name', svgFileName)
|
514 |
svgFilePath = os.path.join(AppDocData.instance().getCurrentProject().getSvgFilePath(), symbol.getType(), svgFileName+'.svg')
|
515 |
svg = SymbolSvgItem.createItem(symbol.getType(), svgFilePath) |
516 |
connPts = None
|
517 |
strConnPts = symbol.getConnectionPoint() |
518 |
if strConnPts is not None: |
519 |
connPts = [(float(x.split(',')[0]), float(x.split(',')[1])) for x in strConnPts.split('/')] |
520 | |
521 |
svg.buildItem(svgFileName, symbol.getType(), 0, None, None, [float(x) for x in symbol.getOriginalPoint().split(',')], connPts, symbol.getBaseSymbol(), symbol.getAdditionalSymbol(), symbol.getHasInstrumentLabel()) |
522 |
|
523 |
return svg
|
524 |
|
525 |
'''
|
526 |
@brief match symbol to line
|
527 |
@author kyouho
|
528 |
@date 2018.07.27
|
529 |
'''
|
530 |
def matchSymbolToLine(self, svg, scenePos): |
531 |
matches = [item for item in self.scene.items() if (type(item) is QEngineeringLineItem) and (item.distanceTo((scenePos.x(), scenePos.y())) < 20)] |
532 |
if len(matches) == 1: |
533 |
matches[0].insertSymbol(svg, scenePos)
|
534 |
else:
|
535 |
transform = QTransform() |
536 |
transform.translate(scenePos.x() - svg.symbolOrigin[0], scenePos.y() - svg.symbolOrigin[1]) |
537 |
svg.setTransform(transform) |
538 |
svg.loc = [scenePos.x() - svg.symbolOrigin[0], scenePos.y() - svg.symbolOrigin[1]] |
539 |
svg.size = [svg.boundingRect().width(), svg.boundingRect().height()] |
540 |
self.scene.addItem(svg)
|
541 | |
542 |
svg.transfer.onRemoved.connect(self.mainWindow.itemRemoved)
|
543 | |
544 |
self.scene.clearFocus()
|
545 |
for item in self.scene.selectedItems(): |
546 |
item.setSelected(False)
|
547 | |
548 |
self.setFocus()
|
549 |
svg.setSelected(True)
|
550 |
self.scene.setFocusItem(svg)
|
551 | |
552 |
'''
|
553 |
@brief find item by uid (SymbolSvgItem 기반, QEngineeringConnectorItem 제외, QEngineeringLineItem 포함)
|
554 |
@author kyouho
|
555 |
@date 2018.07.31
|
556 |
'''
|
557 |
def findItemByUid(self, uid): |
558 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
559 |
items = [item for item in self.scene.items() if hasattr(item, 'uid')] |
560 |
for item in items: |
561 |
if item.uid == uid:
|
562 |
return item
|
563 |
|
564 |
return None |
565 |
|
566 | |
567 | |
568 |
if __name__ == '__main__': |
569 |
import sys |
570 |
try:
|
571 |
from PyQt5.QtWidgets import QApplication |
572 |
except ImportError: |
573 |
try:
|
574 |
from PyQt4.QtGui import QApplication |
575 |
except ImportError: |
576 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
577 |
print('Using Qt ' + QT_VERSION_STR)
|
578 | |
579 |
def handleLeftClick(x, y): |
580 |
row = int(y)
|
581 |
column = int(x)
|
582 |
print("Clicked on image pixel (row="+str(row)+", column="+str(column)+")") |
583 | |
584 |
# Create the application.
|
585 |
app = QApplication(sys.argv) |
586 | |
587 |
# Create image viewer and load an image file to display.
|
588 |
viewer = QtImageViewer(None)
|
589 |
viewer.loadImageFromFile() # Pops up file dialog.
|
590 | |
591 |
# Handle left mouse clicks with custom slot.
|
592 |
viewer.leftMouseButtonPressed.connect(handleLeftClick) |
593 | |
594 |
# Show viewer and run application.
|
595 |
viewer.show() |
596 |
sys.exit(app.exec_()) |