I am currently learning how to write a GUI in PyQt5 that enable user to draw lines and rectangles in a QGrapchisView Scene and sets a QWebEngineView Widget containing a folium map as background.
Issue:
In order to synchronize QGrapchisView Scene and folium map pan and zoom, I use a Event Filter that passes GraphicsScene events to QWebEngineView Widget.
The QEvent.GraphicsSceneWheel event is passed as expected, but the QEvent.GraphicsSceneMouseRelease, QEvent.GraphicsSceneMousePress and QEvent.GraphicsSceneMouseMove are not pass to the QWebEngineView Widget.
Expected behavior: Selected GraphicsScene events are pass to the QWebEngineView Widget, and it enables to synchronize pan and zoom for both Widgets.
What I have tried so far:
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_())
Question:
Is there any other way of accomplishing this?