hytos / HYTOS / HYTOS / QtImageViewer.py @ d79cbac6
이력 | 보기 | 이력해설 | 다운로드 (25.4 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 EngineeringTextItem import QEngineeringTextItem |
20 |
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem |
21 |
from SymbolSvgItem import SymbolSvgItem |
22 |
|
23 |
__author__ = "Marcel Goldschen-Ohm <marcel.goldschen@gmail.com>"
|
24 |
__version__ = '0.9.0'
|
25 |
|
26 |
|
27 |
class QtImageViewer(QGraphicsView): |
28 |
""" PyQt image viewer widget for a QPixmap in a QGraphicsView scene with mouse zooming and panning.
|
29 |
Displays a QImage or QPixmap (QImage is internally converted to a QPixmap).
|
30 |
To display any other image format, you must first convert it to a QImage or QPixmap.
|
31 |
Some useful image format conversion utilities:
|
32 |
qimage2ndarray: NumPy ndarray <==> QImage (https://github.com/hmeine/qimage2ndarray)
|
33 |
ImageQt: PIL Image <==> QImage (https://github.com/python-pillow/Pillow/blob/master/PIL/ImageQt.py)
|
34 |
Mouse interaction:
|
35 |
Left mouse button drag: Pan image.
|
36 |
Right mouse button drag: Zoom box.
|
37 |
Right mouse button doubleclick: Zoom to show entire image.
|
38 |
"""
|
39 |
|
40 |
# Mouse button signals emit image scene (x, y) coordinates.
|
41 |
# !!! For image (row, column) matrix indexing, row = y and column = x.
|
42 |
leftMouseButtonPressed = pyqtSignal(float, float) |
43 |
rightMouseButtonPressed = pyqtSignal(float, float) |
44 |
leftMouseButtonMoved = pyqtSignal(float, float) |
45 |
rightMouseButtonMoved = pyqtSignal(float, float) |
46 |
leftMouseButtonReleased = pyqtSignal(float, float) |
47 |
rightMouseButtonReleased = pyqtSignal(float, float) |
48 |
leftMouseButtonDoubleClicked = pyqtSignal(float, float) |
49 |
rightMouseButtonDoubleClicked = pyqtSignal(float, float) |
50 |
#itemRemoved = pyqtSignal(QGraphicsItem)
|
51 |
startPointChanged = pyqtSignal(float, float) |
52 |
|
53 |
'''
|
54 |
@history 2018.06.27 Jeongwoo Change zoom rule (Qt.KeepAspectRatioByExpanding → Qt.KeepAspectRatio)
|
55 |
'''
|
56 |
def __init__(self, mainWindow = None): |
57 |
from QtImageViewerScene import QtImageViewerScene |
58 |
|
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 = QtImageViewerScene(self) |
65 |
self.setScene(self.scene) |
66 |
self.scene.setBackgroundBrush(Qt.lightGray)
|
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 |
""" Load an image from file.
|
192 |
Without any arguments, loadImageFromFile() will popup a file dialog to choose the image file.
|
193 |
With a fileName argument, loadImageFromFile(fileName) will attempt to load the specified image file directly.
|
194 |
"""
|
195 |
try:
|
196 |
if len(fileName) == 0: |
197 |
options = QFileDialog.Options() |
198 |
options |= QFileDialog.DontUseNativeDialog |
199 |
if QT_VERSION_STR[0] == '4': |
200 |
fileName = QFileDialog.getOpenFileName(self, "Open image file", os.getcwd() if folder == '' else folder, "Image files(*.png *.jpg)", options=options) |
201 |
elif QT_VERSION_STR[0] == '5': |
202 |
fileName, dummy = QFileDialog.getOpenFileName(self, "Open image file", os.getcwd() if folder == '' else folder, "Image files(*.png *.jpg)", options=options) |
203 |
if len(fileName) and os.path.isfile(fileName): |
204 |
image = QImage(fileName, format = None)
|
205 |
self.setImage(image)
|
206 |
except Exception as ex: |
207 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
208 |
|
209 |
return fileName
|
210 |
|
211 |
'''
|
212 |
@history 2018.06.27 Jeongwoo Change zoom rule (Qt.KeepAspectRatioByExpanding → Qt.KeepAspectRatio)
|
213 |
'''
|
214 |
def updateViewer(self, zoomNewRect=None): |
215 |
""" Show current zoom (if showing entire image, apply current aspect ratio mode).
|
216 |
"""
|
217 |
if not self.hasImage(): |
218 |
return
|
219 |
if len(self.zoomStack):# and self.sceneRect().contains(self.zoomStack[-1]): |
220 |
if zoomNewRect is None: |
221 |
self.fitInView(self.zoomStack[-1], Qt.KeepAspectRatio) # Show zoomed rect (ignore aspect ratio). |
222 |
else:
|
223 |
self.fitInView(zoomNewRect, Qt.KeepAspectRatio)
|
224 |
else:
|
225 |
self.zoomStack = [] # Clear the zoom stack (in case we got here because of an invalid zoom). |
226 |
self.fitInView(self.sceneRect(), self.aspectRatioMode) # Show entire image (use current aspect ratio mode). |
227 |
|
228 |
def zoomImageInit(self): |
229 |
if self.hasImage(): |
230 |
self.zoomStack = []
|
231 |
self.updateViewer()
|
232 |
self.setCursor(QCursor(Qt.ArrowCursor))
|
233 |
|
234 |
'''
|
235 |
@brief Zoom in & out image
|
236 |
@author Jeongwoo
|
237 |
@date -
|
238 |
@history 18.04.11 Jeongwoo add parameter 'adjust' (@ref ResultTreeWidget.itemClickEvent(self, item, columnNo))
|
239 |
'''
|
240 |
def zoomImage(self, isZoomIn, event, adjust = 1): |
241 |
""" Zoom in & out
|
242 |
"""
|
243 |
|
244 |
HALF_SIZE = 300
|
245 |
clickPos = event.pos() |
246 |
scenePos1 = self.mapToScene(clickPos.x() - HALF_SIZE//adjust, clickPos.y() - HALF_SIZE//adjust)
|
247 |
scenePos2 = self.mapToScene(clickPos.x() + HALF_SIZE//adjust, clickPos.y() + HALF_SIZE//adjust)
|
248 |
if isZoomIn:
|
249 |
zoomArea = QRectF(QPointF(scenePos1.x() if scenePos1.x() > 0 else 0, scenePos1.y() if scenePos1.y() > 0 else 0), QPointF(scenePos2.x(), scenePos2.y())) |
250 |
#self.fitInView(zoomArea, Qt.KeepAspectRatioByExpanding)
|
251 |
viewBBox = self.zoomStack[-1] if len(self.zoomStack) else self.sceneRect() |
252 |
selectionBBox = zoomArea.intersected(viewBBox) |
253 |
self.scene.setSelectionArea(QPainterPath()) # Clear current selection area. |
254 |
if selectionBBox.width() > HALF_SIZE*2 and selectionBBox.height() > HALF_SIZE*2: |
255 |
if selectionBBox.isValid() and (selectionBBox != viewBBox): |
256 |
self.zoomStack.append(selectionBBox)
|
257 |
self.updateViewer()
|
258 |
else:
|
259 |
zoomNewRect = None
|
260 |
self.scene.setSelectionArea(QPainterPath()) # Clear current selection area. |
261 |
if len(self.zoomStack): |
262 |
self.zoomStack.pop()
|
263 |
if len(self.zoomStack): |
264 |
newScenePos = self.mapToScene(clickPos.x(), clickPos.y())
|
265 |
newPosX1 = newScenePos.x() - self.zoomStack[-1].width() / 2 |
266 |
newPosY1 = newScenePos.y() - self.zoomStack[-1].height() / 2 |
267 |
zoomNewPos1 = QPointF(newPosX1 if newPosX1 > 0 else 0, newPosY1 if newPosY1 > 0 else 0) |
268 |
newPosX2 = newScenePos.x() + self.zoomStack[-1].width() / 2 |
269 |
newPosY2 = newScenePos.y() + self.zoomStack[-1].width() / 2 |
270 |
zoomNewPos2 = QPointF(newPosX2, newPosY2) |
271 |
zoomNewRect = QRectF(zoomNewPos1, zoomNewPos2) |
272 |
self.updateViewer(zoomNewRect)
|
273 |
|
274 |
def resizeEvent(self, event): |
275 |
""" Maintain current zoom on resize.
|
276 |
"""
|
277 |
self.updateViewer()
|
278 |
|
279 |
'''
|
280 |
@brief mouse move event
|
281 |
'''
|
282 |
def mouseMoveEvent(self, event): |
283 |
try:
|
284 |
scenePos = self.mapToScene(event.pos())
|
285 |
if self.command is not None: |
286 |
self.command.execute(['mouseMoveEvent', event, scenePos]) |
287 |
if self.command.name == "SelectAttribute": |
288 |
QGraphicsView.mouseMoveEvent(self, event)
|
289 |
if self.command.isTreated == True: return |
290 |
except Exception as ex: |
291 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
292 |
|
293 |
if self.guidesEnabled: |
294 |
self.coords = self.mapToScene(event.pos()) |
295 |
self.scene.invalidate()
|
296 |
|
297 |
QGraphicsView.mouseMoveEvent(self, event)
|
298 |
|
299 |
'''
|
300 |
@brief
|
301 |
@author
|
302 |
@date
|
303 |
@history block clear selection when right mouse button is clicked
|
304 |
'''
|
305 |
def mousePressEvent(self, event): |
306 |
try:
|
307 |
if self.command is not None: |
308 |
scenePos = self.mapToScene(event.pos())
|
309 |
self.command.execute(['mousePressEvent', event, scenePos]) |
310 |
if self.command.isTreated == True: return |
311 |
except Exception as ex: |
312 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
313 |
|
314 |
if event.button() != Qt.RightButton:
|
315 |
QGraphicsView.mousePressEvent(self, event)
|
316 |
|
317 |
'''
|
318 |
@brief
|
319 |
@author
|
320 |
@date
|
321 |
'''
|
322 |
def mouseReleaseEvent(self, event): |
323 |
try:
|
324 |
if self.command is not None: |
325 |
scenePos = self.mapToScene(event.pos())
|
326 |
instance = self.command.execute(['mouseReleaseEvent', event, scenePos]) |
327 |
if instance is not None: |
328 |
self.scene.addItem(instance)
|
329 |
|
330 |
if self.command is not None and self.command.isTreated == True: |
331 |
if self.command.name == 'Default' and self.command.isCopy: |
332 |
return
|
333 |
self.command = DefaultCommand.DefaultCommand(self) |
334 |
cursor = QCursor(Qt.ArrowCursor) |
335 |
QApplication.instance().setOverrideCursor(cursor) |
336 |
return
|
337 |
except Exception as ex: |
338 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
339 |
|
340 |
QGraphicsView.mouseReleaseEvent(self, event)
|
341 |
|
342 |
"""
|
343 |
@brief Show entire image.
|
344 |
"""
|
345 |
def mouseDoubleClickEvent(self, event): |
346 |
scenePos = self.mapToScene(event.pos())
|
347 |
if self.command is not None: |
348 |
instance = self.command.execute(['mouseDoubleClickEvent', event, scenePos]) |
349 |
if self.command.isTreated == True: return |
350 |
|
351 |
if event.button() == Qt.LeftButton:
|
352 |
self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
|
353 |
elif event.button() == Qt.RightButton:
|
354 |
if self.canZoom: |
355 |
self.zoomStack = [] # Clear zoom stack. |
356 |
self.updateViewer()
|
357 |
self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
|
358 |
|
359 |
QGraphicsView.mouseDoubleClickEvent(self, event)
|
360 |
|
361 |
'''
|
362 |
@brief key press event
|
363 |
@author Jeongwoo
|
364 |
@date 2018.??.??
|
365 |
@history send escape key event to command
|
366 |
|
367 |
def keyPressEvent(self, event):
|
368 |
try:
|
369 |
if event.key() == Qt.Key_Delete:
|
370 |
for item in [item for item in self.scene.selectedItems() if hasattr(item, 'transfer')]:
|
371 |
item.transfer.onRemoved.emit(item)
|
372 |
elif event.key() == Qt.Key_Escape:
|
373 |
if self.command is not None:
|
374 |
self.command.execute(['keyPressEvent', event, []])
|
375 |
if self.command.isTreated: return
|
376 |
else:
|
377 |
if self.command is not None:
|
378 |
self.command.execute(['keyPressEvent', event, []])
|
379 |
if self.command.isTreated: return
|
380 |
|
381 |
QGraphicsView.keyPressEvent(self, event)
|
382 |
except Exception as ex:
|
383 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
|
384 |
'''
|
385 |
|
386 |
'''
|
387 |
@brief key release event
|
388 |
@author Jeongwoo
|
389 |
@date 2018.??.??
|
390 |
'''
|
391 |
def keyReleaseEvent(self, event): |
392 |
if event.key() == Qt.Key_Delete:
|
393 |
pass
|
394 |
|
395 |
QGraphicsView.keyReleaseEvent(self, event)
|
396 |
|
397 |
'''
|
398 |
@brief mouse wheel event
|
399 |
@autor humkyung
|
400 |
@date
|
401 |
'''
|
402 |
def wheelEvent(self, event): |
403 |
if event.modifiers() == Qt.ControlModifier:
|
404 |
if self.canZoom and self.hasImage(): |
405 |
numDegrees = event.angleDelta() / 8
|
406 |
if numDegrees is not None: |
407 |
if numDegrees.y() > 0: |
408 |
self.zoomImage(True, event) |
409 |
elif numDegrees.y() < 0: |
410 |
self.zoomImage(False, event) |
411 |
else:
|
412 |
super().wheelEvent(event)
|
413 |
|
414 |
'''
|
415 |
@brief
|
416 |
'''
|
417 |
def drawForeground(self, painter, rect): |
418 |
if hasattr(self, 'coords') and self.guidesEnabled: |
419 |
painter.setClipRect(rect) |
420 |
painter.setPen(self._guidePen)
|
421 |
painter.drawLine(round(self.coords.x()), rect.top(), round(self.coords.x()), rect.bottom()) |
422 |
painter.drawLine(rect.left(), round(self.coords.y()), rect.right(), round(self.coords.y())) |
423 |
|
424 |
'''
|
425 |
image = self.image()
|
426 |
if image is not None:
|
427 |
width = rect.width()
|
428 |
height = rect.height()
|
429 |
if self.crosshairPos is not None:
|
430 |
pen = QPen()
|
431 |
pen.setColor(QColor(180, 180, 180))
|
432 |
pen.setStyle(Qt.DashLine)
|
433 |
pen.setWidthF(0.3)
|
434 |
painter.setClipRect(rect)
|
435 |
painter.setPen(pen)
|
436 |
painter.drawLine(self.crosshairPos.x(), 0, self.crosshairPos.x(), height)#Vertical
|
437 |
painter.drawLine(0, self.crosshairPos.y(), width, self.crosshairPos.y())#Horizontal
|
438 |
#else:
|
439 |
# painter.eraseRect(QRectF(0, 0, width, height))
|
440 |
'''
|
441 |
|
442 |
'''
|
443 |
@brief draw background
|
444 |
@author humkyung
|
445 |
@date 2018.07.23
|
446 |
'''
|
447 |
def drawBackground(self, painter, rect): |
448 |
QGraphicsView.drawBackground(self, painter, rect)
|
449 |
|
450 |
'''
|
451 |
@history 2018.06.11 Jeongwoo Change method to manage guideline items
|
452 |
humkyung 2018.08.28 remove guide lines before drawing
|
453 |
'''
|
454 |
GUIDELINE_ITEMS = [] |
455 |
def showGuideline(self, pos, isShow): |
456 |
image = self.image()
|
457 |
width = image.width() |
458 |
height = image.height() |
459 |
pen = QPen() |
460 |
pen.setColor(QColor(180, 180, 180)) |
461 |
pen.setStyle(Qt.DashLine) |
462 |
pen.setWidthF(0.5)
|
463 |
if isShow:
|
464 |
items = self.scene.items()
|
465 |
for item in self.GUIDELINE_ITEMS: |
466 |
if item in items: |
467 |
self.scene.removeItem(item)
|
468 |
self.GUIDELINE_ITEMS.clear()
|
469 |
|
470 |
if pos is not None: |
471 |
verticalLine = self.scene.addLine(pos.x(), 0, pos.x(), height, pen) |
472 |
horizontalLine = self.scene.addLine(0, pos.y(), width, pos.y(), pen) |
473 |
else:
|
474 |
verticalLine = self.scene.addLine(round(width*0.5), 0, round(width*0.5), height, pen) |
475 |
horizontalLine = self.scene.addLine(0, round(height*0.5), width, round(height*0.5), pen) |
476 |
|
477 |
self.GUIDELINE_ITEMS.append(verticalLine)
|
478 |
self.GUIDELINE_ITEMS.append(horizontalLine)
|
479 |
else:
|
480 |
items = self.scene.items()
|
481 |
for item in self.GUIDELINE_ITEMS: |
482 |
if item in items: |
483 |
self.scene.removeItem(item)
|
484 |
self.GUIDELINE_ITEMS.clear()
|
485 |
|
486 |
'''
|
487 |
@brief drag enter event
|
488 |
@author humkyung
|
489 |
@date 2018.04.17
|
490 |
'''
|
491 |
def dragEnterEvent(self, event): |
492 |
event.acceptProposedAction() |
493 |
|
494 |
'''
|
495 |
@brief drag move event
|
496 |
@author humkyung
|
497 |
@date 2018.04.17
|
498 |
@history humkyung 2018.08.21 highlight item under mouse
|
499 |
'''
|
500 |
def dragMoveEvent(self, event): |
501 |
scenePos = self.mapToScene(event.pos())
|
502 |
items = [item for item in self.scene.items(scenePos) if type(item) is not QGraphicsPixmapItem and type(item) is not QGraphicsTextItem] |
503 |
if len(items) > 0: |
504 |
if not hasattr(self, '_underItem') or self._underItem is not items[0]: |
505 |
if hasattr(self, '_underItem') and self._underItem is not None: |
506 |
if hasattr(self._underItem, 'highlight'): |
507 |
self._underItem.highlight(False) |
508 |
|
509 |
self._underItem = items[0] |
510 |
if hasattr(self._underItem, 'highlight'): |
511 |
self._underItem.highlight(True) |
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 |
uid = event.mimeData().text() |
537 |
svg = self.createSymbolObject(uid)
|
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, uid): |
556 |
from AppDocData import AppDocData |
557 |
import symbol |
558 |
|
559 |
symbol = AppDocData.instance().getSymbolByQuery('uid', uid)
|
560 |
svgFileName = symbol.getName() |
561 |
svgFilePath = os.path.join(AppDocData.instance().getCurrentProject().getSvgFilePath(), symbol.getCategory(), symbol.getType(), svgFileName+'.svg')
|
562 |
svg = SymbolSvgItem.createItem(symbol.getType(), svgFilePath) |
563 |
connPts = None
|
564 |
strConnPts = symbol.getConnectionPoint() |
565 |
if strConnPts is not None and strConnPts != '': |
566 |
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])) \ |
567 |
for x in strConnPts.split('/')] |
568 |
|
569 |
svg.buildItem(svgFileName, symbol.getType(), 0, None, None, connPts, uid) |
570 |
|
571 |
return svg
|
572 |
|
573 |
'''
|
574 |
@brief match symbol to line
|
575 |
@author kyouho
|
576 |
@date 2018.07.27
|
577 |
@history humkyung 2018.08.23 change scenePos to connector's center when symbol is placed on connector
|
578 |
'''
|
579 |
def matchSymbolToLine(self, svg, scenePos): |
580 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
581 |
|
582 |
items = [item for item in self.scene.items(scenePos) if type(item) is not QGraphicsPixmapItem] |
583 |
if len(items) > 0 and type(items[0]) is QEngineeringConnectorItem: |
584 |
scenePos = QPointF(items[0].center()[0], items[0].center()[1]) |
585 |
|
586 |
transform = QTransform() |
587 |
transform.translate(scenePos.x() - svg.symbolOrigin[0], scenePos.y() - svg.symbolOrigin[1]) |
588 |
svg.setTransform(transform) |
589 |
svg.loc = [round(scenePos.x() - svg.symbolOrigin[0], 1), round(scenePos.y() - svg.symbolOrigin[1], 1)] |
590 |
svg.size = [svg.boundingRect().width(), svg.boundingRect().height()] |
591 |
svg.origin = [round(scenePos.x(), 1), round(scenePos.y(), 1)] |
592 |
self.scene.addItem(svg)
|
593 |
|
594 |
svg.transfer.onRemoved.connect(self.mainWindow.itemRemoved)
|
595 |
|
596 |
for conn in svg.connectors: |
597 |
conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y()) |
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 |
|
615 |
items = [item for item in self.scene.items() if hasattr(item, 'uid') and str(item.uid) == str(uid)] |
616 |
return items[0] if items else None |
617 |
|
618 |
if __name__ == '__main__': |
619 |
import sys |
620 |
try:
|
621 |
from PyQt5.QtWidgets import QApplication |
622 |
except ImportError: |
623 |
try:
|
624 |
from PyQt4.QtGui import QApplication |
625 |
except ImportError: |
626 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
627 |
print('Using Qt ' + QT_VERSION_STR)
|
628 |
|
629 |
def handleLeftClick(x, y): |
630 |
row = int(y)
|
631 |
column = int(x)
|
632 |
print("Clicked on image pixel (row="+str(row)+", column="+str(column)+")") |
633 |
|
634 |
# Create the application.
|
635 |
app = QApplication(sys.argv) |
636 |
|
637 |
# Create image viewer and load an image file to display.
|
638 |
viewer = QtImageViewer(None)
|
639 |
viewer.loadImageFromFile() # Pops up file dialog.
|
640 |
|
641 |
# Handle left mouse clicks with custom slot.
|
642 |
viewer.leftMouseButtonPressed.connect(handleLeftClick) |
643 |
|
644 |
# Show viewer and run application.
|
645 |
viewer.show() |
646 |
sys.exit(app.exec_()) |