简体   繁体   中英

GraphicsView Scene postEvent focusProxy does not pass events from event filter to QWebEngineView Widget

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?

It appears that the OP tries to create the events by taking custom parameters unnecessarily. In my solution I propose to just copy the parameters:

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_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM