python - GraphicsView 场景 postEvent focusProxy 不会将事件从事件过滤器传递到 QWebEngineView Widget
问题描述
我目前正在学习如何在 PyQt5 中编写一个 GUI,使用户能够在 a 中绘制线条和矩形,QGrapchisView Scene
并将一个QWebEngineView
包含 folium 地图的小部件设置为背景。
问题:
为了同步 QGrapchisView
场景和叶图平移和缩放,我使用事件过滤器将GraphicsScene
事件传递给 QWebEngineView
小部件。
该QEvent.GraphicsSceneWheel
事件按预期传递,但 QEvent.GraphicsSceneMouseRelease
,QEvent.GraphicsSceneMousePress
和QEvent.GraphicsSceneMouseMove
并没有传递给QWebEngineView
Widget。
预期行为: 选定的 GraphicsScene 事件被传递给 QWebEngineView 小部件,它可以同步两个小部件的平移和缩放。
到目前为止我已经尝试过:
import folium
import sys
from PyQt5 import QtGui, QtCore, QtWidgets, QtWebEngineWidgets
import io
class eventFilterClass(QtCore.QObject):
def __init__(self, sender, receiver, gv):
super(eventFilterClass, self).__init__()
self.gv = gv
self.m_sender = sender
self.m_receiver = receiver
self.m_sender.installEventFilter(self)
def eventFilter(self, obj, event):
if self.m_sender is obj:
if event.type() == QtCore.QEvent.GraphicsSceneMousePress:
if event.button() == QtCore.Qt.MiddleButton:
new_event = QtGui.QMouseEvent(int(2), self.gv.mapFromScene(event.scenePos()),
self.gv.mapFromScene(event.scenePos()), event.screenPos(), event.button(),
event.buttons(), event.modifiers(), event.source())
QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
return True
elif event.type() == QtCore.QEvent.GraphicsSceneMouseMove:
if event.buttons() == QtCore.Qt.NoButton:
new_event = QtGui.QMouseEvent(int(5), self.gv.mapFromScene(event.scenePos()),self.gv.mapFromScene(event.scenePos()), event.screenPos(), event.button(), event.buttons(), event.modifiers(), event.source())
QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
return True
elif event.type() == QtCore.QEvent.GraphicsSceneMouseRelease:
print('Release!')
if event.button() == QtCore.Qt.MiddleButton:
new_event = QtGui.QMouseEvent(int(3), self.gv.mapFromScene(event.scenePos()),
self.gv.mapFromScene(event.scenePos()), event.screenPos(), event.button(),
event.buttons(), event.modifiers(), event.source())
QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
elif event.type() == QtCore.QEvent.GraphicsSceneWheel:
new_event = QtGui.QWheelEvent(self.gv.mapFromScene(event.scenePos()), event.screenPos(), QtCore.QPoint(), QtCore.QPoint(0, event.delta()),event.buttons(), event.modifiers(), int(0), False, int(0))
QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
return True
return False
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent, scene=None):
super(GraphicsView, self).__init__(scene, parent)
"VARIABLES INICIALES"
self.pos_init_class = None
self.scale_factor = 1.25
"ACTIVAR TRACKING DE POSICION DE MOUSE"
self.setMouseTracking(True)
"REMOVER BARRAS DE SCROLL"
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
"ASIGNAR ANCLA PARA HACER ZOOM SOBRE EL MISMO PUNTO"
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
self.scale(self.scale_factor, self.scale_factor)
else:
self.scale(1 / self.scale_factor, 1 / self.scale_factor)
super(GraphicsView, self).wheelEvent(event)
def mousePressEvent(self, event):
pos = self.mapToScene(event.pos())
"PAN"
if event.button() == QtCore.Qt.MiddleButton:
self.pos_init_class = pos
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.ClosedHandCursor)
super(GraphicsView, self).mousePressEvent(event)
return
super(GraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
pos = self.mapToScene(event.pos())
"PAN"
if self.pos_init_class:
delta = self.pos_init_class - self.mapToScene(event.pos())
r = self.mapToScene(self.viewport().rect()).boundingRect()
self.setSceneRect(r.translated(delta))
return
super(GraphicsView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
"PAN"
if self.pos_init_class and event.button() == QtCore.Qt.MiddleButton:
self.pos_init_class = None
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.ArrowCursor)
super(GraphicsView, self).mouseReleaseEvent(event)
class Ui_MainWindow(object):
def __init__(self):
super(Ui_MainWindow, self).__init__()
def zoom_ext(self):
"Zoom extent"
x_range, y_range, h_range, w_range = self.graphicsView.scene().itemsBoundingRect().getRect()
rect_scene = QtCore.QRectF(x_range, y_range, h_range, w_range)
self.graphicsView.setSceneRect(rect_scene)
unity = self.graphicsView.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.graphicsView.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.graphicsView.viewport().rect()
scenerect = self.graphicsView.transform().mapRect(rect_scene)
factor = min(viewrect.width() / scenerect.width(), viewrect.height() / scenerect.height())
self.graphicsView.scale(factor, factor)
def draw_lines(self):
cancha = QtGui.QPolygonF([
QtCore.QPointF(721383.8266, -9678885.4514),
QtCore.QPointF(721404.5488, -9678961.6564),
QtCore.QPointF(721453.4389, -9678948.7816),
QtCore.QPointF(721433.20288, -9678871.92070)])
self.graphicsView.scene().addPolygon(cancha, QtGui.QPen(QtCore.Qt.red, 2.5, QtCore.Qt.SolidLine))
caseta = QtGui.QPolygonF([
QtCore.QPointF(721455.8594, -9678925.4517),
QtCore.QPointF(721492.5411, -9678915.2403),
QtCore.QPointF(721498.3404, -9678937.9702),
QtCore.QPointF(721461.7904, -9678947.5050)])
self.graphicsView.scene().addPolygon(caseta, QtGui.QPen(QtCore.Qt.red, 2.5, QtCore.Qt.SolidLine))
def setupUi(self, MainWindow):
"CENTRAL WIDGET"
self.centralwidget = QtWidgets.QWidget(MainWindow)
MainWindow.setCentralWidget(self.centralwidget)
MainWindow.resize(1000, 1000)
"WEB MAP"
self.w = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
self.w.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
"QGRAPCHISVIEW SCENE"
self.graphicsView = GraphicsView(parent=self.centralwidget)
self.scene = QtWidgets.QGraphicsScene(parent=self.graphicsView)
self.graphicsView.setScene(self.scene)
self.graphicsView.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
self.graphicsView.setStyleSheet("background:transparent;")
"Draw LINES"
self.draw_lines()
"ZOOM EXT"
self.zoom_ext()
"AGREAGR MAPA"
self.m = folium.Map(tiles='http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', attr='Google Satellite', zoomSnap=0.0, wheelDebounceTime=-1,
wheelPxPerZoomLevel=105, prefer_canvas=True, control_scale=True, max_zoom=100000,
zoomControl=False)
sw, ne = (-2.903683906544795, -79.00835706455769), (-2.9026234455284583, -79.00729860799157)
self.m.fit_bounds([sw, ne], padding=(0, 0))
data = io.BytesIO()
self.m.save(data, close_file=False)
self.w.setHtml(data.getvalue().decode())
"APLICAR FILTER"
self.efc = eventFilterClass(sender=self.graphicsView.scene(), receiver=self.w, gv=self.graphicsView)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
问题:
有没有其他方法可以做到这一点?
解决方案
看来 OP 试图通过不必要地采用自定义参数来创建事件。在我的解决方案中,我建议只复制参数:
import io
import sys
from PyQt5 import QtGui, QtCore, QtWidgets, QtWebEngineWidgets
import folium
class ReplyEvents(QtCore.QObject):
def __init__(self, sender, receiver, parent=None):
super().__init__(parent)
self._sender = sender
self._receiver = receiver
self._sender.installEventFilter(self)
def eventFilter(self, obj, event):
if obj is self._sender:
new_event = None
if event.type() in (
QtCore.QEvent.MouseButtonPress,
QtCore.QEvent.MouseButtonRelease,
QtCore.QEvent.MouseButtonDblClick,
QtCore.QEvent.MouseMove,
):
new_event = QtGui.QMouseEvent(
event.type(),
event.pos(),
event.button(),
event.buttons(),
event.modifiers(),
)
elif event.type() == QtCore.QEvent.Wheel:
new_event = QtGui.QWheelEvent(
event.pos(),
event.globalPosition(),
event.pixelDelta(),
event.angleDelta(),
event.buttons(),
event.modifiers(),
event.phase(),
event.inverted(),
)
if new_event:
QtCore.QCoreApplication.postEvent(self._receiver, new_event)
return super().eventFilter(obj, event)
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.scale_factor = 1.25
self.pos_init_class = QtCore.QPointF()
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
self.setStyleSheet("background:transparent;")
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setMouseTracking(True)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
self.scale(self.scale_factor, self.scale_factor)
else:
self.scale(1 / self.scale_factor, 1 / self.scale_factor)
super().wheelEvent(event)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton:
self.pos_init_class = self.mapToScene(event.pos())
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.ClosedHandCursor)
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if not self.pos_init_class.isNull():
delta = self.pos_init_class - self.mapToScene(event.pos())
r = self.mapToScene(self.viewport().rect()).boundingRect()
self.setSceneRect(r.translated(delta))
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if not self.pos_init_class.isNull() and event.button() == QtCore.Qt.MiddleButton:
self.pos_init_class = QtCore.QPointF()
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.ArrowCursor)
super().mouseReleaseEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(1000, 1000)
self.web_view = QtWebEngineWidgets.QWebEngineView(self)
self.graphics_view = GraphicsView(self)
geometry = QtCore.QRect(0, 0, 1000, 1000)
self.web_view.setGeometry(geometry)
self.graphics_view.setGeometry(geometry)
self.draw_lines()
self.zoom_ext()
m = folium.Map(
tiles="http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}",
attr="Google Satellite",
zoomSnap=0.0,
wheelDebounceTime=-1,
wheelPxPerZoomLevel=105,
prefer_canvas=True,
control_scale=True,
max_zoom=100000,
zoomControl=False,
)
sw, ne = (
(-2.903683906544795, -79.00835706455769),
(-2.9026234455284583, -79.00729860799157),
)
m.fit_bounds([sw, ne], padding=(0, 0))
data = io.BytesIO()
m.save(data, close_file=False)
self.web_view.setHtml(data.getvalue().decode())
r = ReplyEvents(self.graphics_view.viewport(), self.web_view.focusProxy(), self)
def draw_lines(self):
cancha = QtGui.QPolygonF(
[
QtCore.QPointF(721383.8266, -9678885.4514),
QtCore.QPointF(721404.5488, -9678961.6564),
QtCore.QPointF(721453.4389, -9678948.7816),
QtCore.QPointF(721433.20288, -9678871.92070),
]
)
self.graphics_view.scene().addPolygon(
cancha, QtGui.QPen(QtCore.Qt.red, 2.5, QtCore.Qt.SolidLine)
)
caseta = QtGui.QPolygonF(
[
QtCore.QPointF(721455.8594, -9678925.4517),
QtCore.QPointF(721492.5411, -9678915.2403),
QtCore.QPointF(721498.3404, -9678937.9702),
QtCore.QPointF(721461.7904, -9678947.5050),
]
)
self.graphics_view.scene().addPolygon(
caseta, QtGui.QPen(QtCore.Qt.red, 2.5, QtCore.Qt.SolidLine)
)
def zoom_ext(self):
"Zoom extent"
x_range, y_range, h_range, w_range = (
self.graphics_view.scene().itemsBoundingRect().getRect()
)
rect_scene = QtCore.QRectF(x_range, y_range, h_range, w_range)
self.graphics_view.setSceneRect(rect_scene)
unity = self.graphics_view.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.graphics_view.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.graphics_view.viewport().rect()
scenerect = self.graphics_view.transform().mapRect(rect_scene)
factor = min(
viewrect.width() / scenerect.width(), viewrect.height() / scenerect.height()
)
self.graphics_view.scale(factor, factor)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
推荐阅读
- routes - 这里 API 返回构造很差的过境路线
- kubernetes - 禁用 Prometheus 健康端点的安全性,/-/healthy 和/-/ready 端点在启用基本身份验证时也受到保护
- r - 计算 R iGraph 中包含边的三角形
- java - 使用每秒 5 个许可的 Guava 速率限制器时,执行程序线程池大小应该是多少?
- c++ - 荨麻曲线25519密钥交换
- django - 错误内核内存不足:杀死进程(uwsgi)并消耗过多内存
- c# - 如何在 c# 中获取 3d 对象的任何位置坐标,统一
- vue.js - Nuxt 阿波罗认证
- c - SGDK 前 2 个图块未正确加载
- javascript - 调用数据库时在nodejs中使用sql的promise逻辑