Example #1
0
class ImageView(QGraphicsView):
	# Tell PageWidget that a file is dropped onto view.
	dropped_relay = Signal(QDropEvent)

	def __init__(self, image_path):
		super(ImageView, self).__init__(None)
		self.scene = QGraphicsScene()
		self.setScene(self.scene)
		self.pixmapitem = self.scene.addPixmap(QPixmap.fromImage(QImage(image_path)))
		self.last_release_time = 0
		self.watcher = QFileSystemWatcher()
		self.watcher.fileChanged.connect(self.refresh_image)
		# Register file watcher
		self.watcher.addPath(image_path)

	def dragEnterEvent(self, drag_enter_event): # QDragEnterEvent
		if drag_enter_event.mimeData().hasUrls():
			drag_enter_event.acceptProposedAction()

	# https://stackoverflow.com/a/4421835/4112667
	def dragMoveEvent(self, event):
		pass

	def dropEvent(self, drop_event): # QDropEvent
		self.dropped_relay.emit(drop_event)

	'''
	When overwriting an image file, I guess Windows will delete it and then create
	a new file with the same name. So this function will be called twice. The first
	round is triggered by deleting. In this case, the image file doesn't exist, so
	QImage and QPixmap are all invalid and as a result, the view will become white
	background. Only after the image being created and the function is called for
	the second time, will the view show the image normally. The User will notice a
	white flicker because of two rounds of callings. To resolve this problem, we
	need to detect the invalid QImage or QPixmap and skip the unintended round.
	'''
	def refresh_image(self, image_path):
		qimage = QImage(image_path)
		if qimage.isNull():
			return
		pixmap = QPixmap.fromImage(qimage)
		self.scene.removeItem(self.pixmapitem)
		self.pixmapitem = self.scene.addPixmap(pixmap)
		# This will make scrollbar fit the image
		self.setSceneRect(QRectF(pixmap.rect()))

	def mousePressEvent(self, mouse_event): # QMouseEvent
		if mouse_event.button() == Qt.LeftButton:
			self.setDragMode(QGraphicsView.ScrollHandDrag)
		elif mouse_event.button() == Qt.RightButton:
			self.setDragMode(QGraphicsView.RubberBandDrag)
		QGraphicsView.mousePressEvent(self, mouse_event)

	def mouseReleaseEvent(self, mouse_event): # QMouseEvent
		QGraphicsView.mouseReleaseEvent(self, mouse_event)
		if mouse_event.button() == Qt.LeftButton:
			self.setDragMode(QGraphicsView.NoDrag)
		elif mouse_event.button() == Qt.RightButton:
			self.setDragMode(QGraphicsView.NoDrag)

			now = time.time()
			delta = now - self.last_release_time
			self.last_release_time = now
			if delta < 0.3: # fast double click
				self.resetTransform() # Reset to original size (reset scale matrix)
				return
			# Maybe a selection
			selection = self.scene.selectionArea().boundingRect()
			self.scene.setSelectionArea(QPainterPath())
			if selection.isValid():
				self.fitInView(selection, Qt.KeepAspectRatio)

	def wheelEvent(self, wheel_event): # QWheelEvent
		num_degrees = wheel_event.angleDelta().y() / 8
		num_steps = num_degrees / 15
		coefficient = 1 + (num_steps * 0.25)
		self.scale(coefficient, coefficient)