I want to be able to choose a 360-degree image and show it in a PyQt5 window making it interact-able and moveable, is there any way to do this? I am done with the part of choosing the file but can't seem to display it in a Qlabel.
I am done with the part of choosing the file but can't seem to display it in a Qlabel.
I expect the picture to be interactable like the 360-images shown on Facebook
You cannot set the pixmap for the label, as it would show the full image entirely.
Instead, you will need to create your own QWidget, and provide by yourself "panning" implementation by overriding the paintEvent (which actually draws the image) and mouse events for interaction.
I made a very basic example, which does not support zooming (which would require much more computation). The source image I used is taken from here
It creates a local pixmap which is repeated 3 times horizontally to ensure that you can pan infinitely, uses mouseMoveEvent to compute the position via a QTimer
and limits the vertical position to the image height, while resetting the horizontal position whenever the x coordinate is beyond the half of the image width.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Panoramic(QtWidgets.QWidget):
def __init__(self, imagePath):
QtWidgets.QWidget.__init__(self)
self.setCursor(QtCore.Qt.CrossCursor)
# keep a reference of the original image
self.source = QtGui.QPixmap(imagePath)
self.pano = QtGui.QPixmap(self.source.width() * 3, self.source.height())
self.center = self.pano.rect().center()
# use a QPointF for precision
self.delta = QtCore.QPointF()
self.deltaTimer = QtCore.QTimer(interval=25, timeout=self.moveCenter)
self.sourceRect = QtCore.QRect()
# create a pixmap with three copies of the source;
# this could be avoided by smart repainting and translation of the source
# but since paintEvent automatically clips the painting, it should be
# faster then computing the new rectangle each paint cycle, at the cost
# of a few megabytes of memory.
self.setMaximumSize(self.source.size())
qp = QtGui.QPainter(self.pano)
qp.drawPixmap(0, 0, self.source)
qp.drawPixmap(self.source.width(), 0, self.source)
qp.drawPixmap(self.source.width() * 2, 0, self.source)
qp.end()
def moveCenter(self):
if not self.delta:
return
self.center += self.delta
# limit the vertical position
if self.center.y() < self.sourceRect.height() * .5:
self.center.setY(self.sourceRect.height() * .5)
elif self.center.y() > self.source.height() - self.height() * .5:
self.center.setY(self.source.height() - self.height() * .5)
# reset the horizontal position if beyond the center of the virtual image
if self.center.x() < self.source.width() * .5:
self.center.setX(self.source.width() * 1.5)
elif self.center.x() > self.source.width() * 2.5:
self.center.setX(self.source.width() * 1.5)
self.sourceRect.moveCenter(self.center.toPoint())
self.update()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.mousePos = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.LeftButton:
return
delta = event.pos() - self.mousePos
# use a fraction to get small movements, and ensure we're not too fast
self.delta.setX(max(-25, min(25, delta.x() * .125)))
self.delta.setY(max(-25, min(25, delta.y() * .125)))
if not self.deltaTimer.isActive():
self.deltaTimer.start()
def mouseReleaseEvent(self, event):
self.deltaTimer.stop()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.drawPixmap(self.rect(), self.pano, self.sourceRect)
# resize and reposition the coordinates whenever the window is resized
def resizeEvent(self, event):
self.sourceRect.setSize(self.size())
self.sourceRect.moveCenter(self.center)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Panoramic('pano.jpg')
w.show()
sys.exit(app.exec_())