如何覆盖 pyqt4 QGraphicsView.mousePressEvent?
How can I override a pyqt4 QGraphicsView.mousePressEvent?
我正在尝试在 pyqt4 中调整照片查看器(我从本网站的另一个问题中获得了代码)。默认情况下,鼠标滚轮缩放,鼠标左键用于 pan/drag(不知道具体叫什么)图像。我想添加左键单击图像时获取像素信息的功能,所以我打算有一个按钮可以从 'pan' 模式更改为 'pixel info' 模式。下面是我尝试这样做的尝试,但它没有按我的预期运行。
当我使用第一个 GUI 按钮加载图像并放大图像时,如果我不 pan/drag 图像,则单击第二个 GUI 按钮会禁用 pan/drag 并且,当我点击图像时,为我点击的像素提供像素信息。这是我所期望的。
但是,如果我先放大然后 pan/drag 图像,当我单击第二个 GUI 按钮时,它不再禁用 pan/drag 并且不打印像素信息。
出于某种原因我不明白(我希望有人能启发我)缩放后拖动图像似乎锁定在 pan/drag 方法中并且不允许我更改它。谁能告诉我为什么。
from PyQt4 import QtCore, QtGui
class PhotoViewer(QtGui.QGraphicsView):
def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
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 fitInView(self):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
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.centerOn(rect.center())
self._zoom = 0
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
self.fitInView()
else:
self.setDragMode(QtGui.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
def wheelEvent(self, event):
if not self._photo.pixmap().isNull():
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 printPixInfo(self,event):
print event.pos()
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)
# 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)
VBlayout.addLayout(HBlayout)
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap('pic.jpg'))
def pixInfo(self):
print 'Now in pixel info mode'
self.viewer._photo.mousePressEvent = self.viewer.printPixInfo
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
我还想知道,一旦我成功更改了鼠标单击方法,将鼠标左键单击返回到默认 pan/drag 行为的语法是什么。
我是python新手,请大家不要批评我太多。谢谢
问题在于,通过完全覆盖 mousePressEvent
,您将失去所有默认行为(例如,用鼠标平移)。最好使用自定义信号在照片被点击时发出通知。由于图形视图已经有一个 API 用于更改拖动模式,您可以添加一个用于切换模式的插槽:
class PhotoViewer(QtGui.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
...
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()))
# keep the default behaviour
super(PhotoViewer, self).mousePressEvent(event)
然后可以使用这两个新 API 为您提供所需的功能:
class Window(QtGui.QWidget):
def __init__(self):
...
# add a line-edit to display the position info
self.editPixInfo = QtGui.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
...
HBlayout.addWidget(self.editPixInfo)
def pixInfo(self):
# switch modes
self.viewer.toggleDragMode()
def photoClicked(self, pos):
# handle photo-click signals
if self.viewer.dragMode() == QtGui.QGraphicsView.NoDrag:
self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))
我正在尝试在 pyqt4 中调整照片查看器(我从本网站的另一个问题中获得了代码)。默认情况下,鼠标滚轮缩放,鼠标左键用于 pan/drag(不知道具体叫什么)图像。我想添加左键单击图像时获取像素信息的功能,所以我打算有一个按钮可以从 'pan' 模式更改为 'pixel info' 模式。下面是我尝试这样做的尝试,但它没有按我的预期运行。
当我使用第一个 GUI 按钮加载图像并放大图像时,如果我不 pan/drag 图像,则单击第二个 GUI 按钮会禁用 pan/drag 并且,当我点击图像时,为我点击的像素提供像素信息。这是我所期望的。
但是,如果我先放大然后 pan/drag 图像,当我单击第二个 GUI 按钮时,它不再禁用 pan/drag 并且不打印像素信息。
出于某种原因我不明白(我希望有人能启发我)缩放后拖动图像似乎锁定在 pan/drag 方法中并且不允许我更改它。谁能告诉我为什么。
from PyQt4 import QtCore, QtGui
class PhotoViewer(QtGui.QGraphicsView):
def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
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 fitInView(self):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
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.centerOn(rect.center())
self._zoom = 0
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
self.fitInView()
else:
self.setDragMode(QtGui.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
def wheelEvent(self, event):
if not self._photo.pixmap().isNull():
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 printPixInfo(self,event):
print event.pos()
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)
# 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)
VBlayout.addLayout(HBlayout)
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap('pic.jpg'))
def pixInfo(self):
print 'Now in pixel info mode'
self.viewer._photo.mousePressEvent = self.viewer.printPixInfo
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
我还想知道,一旦我成功更改了鼠标单击方法,将鼠标左键单击返回到默认 pan/drag 行为的语法是什么。
我是python新手,请大家不要批评我太多。谢谢
问题在于,通过完全覆盖 mousePressEvent
,您将失去所有默认行为(例如,用鼠标平移)。最好使用自定义信号在照片被点击时发出通知。由于图形视图已经有一个 API 用于更改拖动模式,您可以添加一个用于切换模式的插槽:
class PhotoViewer(QtGui.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
...
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()))
# keep the default behaviour
super(PhotoViewer, self).mousePressEvent(event)
然后可以使用这两个新 API 为您提供所需的功能:
class Window(QtGui.QWidget):
def __init__(self):
...
# add a line-edit to display the position info
self.editPixInfo = QtGui.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
...
HBlayout.addWidget(self.editPixInfo)
def pixInfo(self):
# switch modes
self.viewer.toggleDragMode()
def photoClicked(self, pos):
# handle photo-click signals
if self.viewer.dragMode() == QtGui.QGraphicsView.NoDrag:
self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))