python - Cómo habilitar Pan y Zoom en un QGraphicsView
python-3.x pyqt4 (2)
Es posible abrir archivos TIFF de hasta varios gigabytes con la biblioteca PIL (almohada) ordinaria. No es del todo fácil, pero funciona.
Puede ver el ejemplo aquí , segundo ejemplo después de que la cadena EDIT negrita podría abrir, mover y hacer zoom en los archivos TIFF.
Estoy usando Python y Qt Designer para implementar la carga de imágenes tiff y para activar Pan y Zoom en algún evento del mouse (rueda - zoom, presione rueda - pan).
Estaba buscando algunas opciones y clases que pueden funcionar con imágenes, etc., y hasta ahora he encontrado:
QGraphicsScene, QImage, QGraphicsView
Tengo tres clases (solo pruebas)
-
ViewerDemo que tiene el elemento QGraphicsView :
"""description of class""" # Form implementation generated from reading ui file ''GraphicsViewdemo.ui'' try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName(("Dialog")) Dialog.resize(500, 500) self.graphicsView = QtGui.QGraphicsView(Dialog) self.graphicsView.setGeometry(QtCore.QRect(0, 0, 500, 500)) self.graphicsView.setObjectName(("graphicsView")) self.retranslateUi(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
-
Clase MyForm , que es QDialog , donde llamo a la clase ViewerDemo , cargando Imagen y pongo la imagen en QGraphicsView
import sys from ViewerDemo import * from PyQt4 import QtGui class MyForm(QtGui.QDialog): def __init__(self, url, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = Ui_Dialog() self.ui.setupUi(self) self.scene = QtGui.QGraphicsScene(self) self.image = QtGui.QImage(url) pixmap= QtGui.QPixmap.fromImage(self.image) item=QtGui.QGraphicsPixmapItem(pixmap) self.scene.addItem(item) self.ui.graphicsView.setScene(self.scene) self.scale = 1 QtCore.QObject.connect(self.scene, QtCore.SIGNAL(''mousePressEvent()''),self.mousePressEvent) def mousePressEvent(self, event): print (''PRESSED : '',event.pos())
(3) es justo donde se ejecuta la aplicación:
from PyQt4 import QtGui, QtCore
import sys
from MyForm import MyForm
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
url = "D:/probaTiff"
myapp = MyForm(url)
myapp.show()
sys.exit(app.exec_())
Encontré cómo hacer algo al hacer clic con el mouse (clic izquierdo y rueda), para imprimir coordenadas de píxeles (lo necesitaré para obtener las coordenadas en el Sistema de coordenadas de la imagen WGS84, por ejemplo).
Lo que necesito más es cómo hacer zoom en la imagen (rueda o doble clic, lo que sea) y encuadrar la imagen (presionando el botón izquierdo del mouse o presionando la rueda).
O, ¿hay algunas mejores clases de Qt para hacer esto, y alguna forma mejor? ¿Me pueden ayudar por favor?
Esto es lo que tengo hasta ahora con este código
Esto no es demasiado difícil de hacer usando las capacidades
QGraphicsView
de
QGraphicsView
.
La secuencia de comandos de demostración a continuación tiene desplazamiento panorámico con el botón izquierdo y zoom de la rueda (incluido el anclaje a la posición actual del cursor).
El método
fitInView
se ha vuelto a implementar porque la versión incorporada agrega un margen fijo extraño que no se puede eliminar.
Versión PyQt4:
from PyQt4 import QtCore, QtGui
class PhotoViewer(QtGui.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
self._empty = True
self._scene = QtGui.QGraphicsScene(self)
self._photo = QtGui.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtGui.QFrame.NoFrame)
def hasPhoto(self):
return not self._empty
def fitInView(self, scale=True):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
else:
self._empty = True
self.setDragMode(QtGui.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
self.fitInView()
def wheelEvent(self, event):
if self.hasPhoto():
if event.delta() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def toggleDragMode(self):
if self.dragMode() == QtGui.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtGui.QGraphicsView.NoDrag)
elif not self._photo.pixmap().isNull():
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
def mousePressEvent(self, event):
if self._photo.isUnderMouse():
self.photoClicked.emit(QtCore.QPoint(event.pos()))
super(PhotoViewer, self).mousePressEvent(event)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = PhotoViewer(self)
# ''Load image'' button
self.btnLoad = QtGui.QToolButton(self)
self.btnLoad.setText(''Load image'')
self.btnLoad.clicked.connect(self.loadImage)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtGui.QToolButton(self)
self.btnPixInfo.setText(''Enter pixel info mode'')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtGui.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtGui.QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QtGui.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout)
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap(''image.jpg''))
def pixInfo(self):
self.viewer.toggleDragMode()
def photoClicked(self, pos):
if self.viewer.dragMode() == QtGui.QGraphicsView.NoDrag:
self.editPixInfo.setText(''%d, %d'' % (pos.x(), pos.y()))
if __name__ == ''__main__'':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
Versión PyQt5:
from PyQt5 import QtCore, QtGui, QtWidgets
class PhotoViewer(QtWidgets.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
self._empty = True
self._scene = QtWidgets.QGraphicsScene(self)
self._photo = QtWidgets.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtWidgets.QFrame.NoFrame)
def hasPhoto(self):
return not self._empty
def fitInView(self, scale=True):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
else:
self._empty = True
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
self.fitInView()
def wheelEvent(self, event):
if self.hasPhoto():
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def toggleDragMode(self):
if self.dragMode() == QtWidgets.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
elif not self._photo.pixmap().isNull():
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
def mousePressEvent(self, event):
if self._photo.isUnderMouse():
self.photoClicked.emit(QtCore.QPoint(event.pos()))
super(PhotoViewer, self).mousePressEvent(event)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = PhotoViewer(self)
# ''Load image'' button
self.btnLoad = QtWidgets.QToolButton(self)
self.btnLoad.setText(''Load image'')
self.btnLoad.clicked.connect(self.loadImage)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtWidgets.QToolButton(self)
self.btnPixInfo.setText(''Enter pixel info mode'')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtWidgets.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtWidgets.QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QtWidgets.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout)
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap(''image.jpg''))
def pixInfo(self):
self.viewer.toggleDragMode()
def photoClicked(self, pos):
if self.viewer.dragMode() == QtWidgets.QGraphicsView.NoDrag:
self.editPixInfo.setText(''%d, %d'' % (pos.x(), pos.y()))
if __name__ == ''__main__'':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())