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