pythonsubclasspysideqgraphicsvieweventfilter

PySide: event-filter for QGraphicsView in container class


I have communications working between similar widgets from imported child widgets and the parent main window and widgets. However, I am stumped when it comes to a QGraphicsScene widget imported as a module and sub-widget. I have put some simplified files below. So, QGraphicsView (from the QGraphicsScene) will be the actual widget I need to emit and signal events to other QWidgets inside the main window.

If I have all classes in one file, it works but if I have the classes as separate modules, I get "does not have attribute" errors, specifically in the simple version here for QGraphicsScene .viewport

Attribute Error "self.graphicsView.viewport().installEventFilter(self)"

I guess that the composite graphics widget is actually now a QWidget and I am not initialising the imported module functions/attributes for the QGraphicsView element. Thing is, I want it to exist that way so I can separate the GUI elements and functions of different modules. The others I have used so far have been straightforward QWidget to QWidget signals derived from QObjects, so work fine, but I haven't been able to achieve the same with the imported QGraphicsScene to QWidgets since it errors when it tries to reach QGraphicsView within the main window. Again, all fine if all classes exist in one large file.

Any kind person can point out my error here? How can I separate the module scripts to behave the same way as the single script?

Working single script:

# QWidgetAll.py
from PySide import QtGui, QtCore

class GraphicsView(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.graphicsView = QtGui.QGraphicsView(self)
        self.graphicsLabel = QtGui.QLabel("Graphics View within QWidget")
        self.graphicsView.setMouseTracking(True)
        self.graphicsView.viewport().installEventFilter(self)
        self.edit = QtGui.QLineEdit(self)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.graphicsLabel)
        layout.addWidget(self.edit)      
        layout.addWidget(self.graphicsView)

    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.MouseMove and
            source is self.graphicsView.viewport()):
            pos = event.pos()
            self.edit.setText('x: %d, y: %d' % (pos.x(), pos.y()))
        return QtGui.QWidget.eventFilter(self, source, event)

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    window = GraphicsView()
    window.show()
    window.resize(200, 100)
    sys.exit(app.exec_())

The same file as separate modules. qWidgetView.py errors with attribute error:

# qWidgetView.py
from PySide import QtGui, QtCore
from qGraphicView import GraphicsView

class WidgetView(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.graphicsView = GraphicsView()
        self.graphicsView.setMouseTracking(True)
        self.graphicsView.viewport().installEventFilter(self)
        self.edit = QtGui.QLineEdit(self)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.edit)      
        layout.addWidget(self.graphicsView)

    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.MouseMove and
            source is self.graphicsView.viewport()):
            pos = event.pos()
            self.edit.setText('x: %d, y: %d' % (pos.x(), pos.y()))
        return QtGui.QWidget.eventFilter(self, source, event)

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    window = WidgetView()
    window.show()
    window.resize(200, 100)
    sys.exit(app.exec_())

with imported qGraphicView.py module:

# qGraphicView.py
from PySide import QtGui, QtCore

class GraphicsView(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.graphicsView = QtGui.QGraphicsView(self)
        self.graphicsLabel = QtGui.QLabel("Graphics View within QWidget")
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.graphicsLabel) 
        layout.addWidget(self.graphicsView)

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    window = GraphicsView()
    window.show()
    window.resize(200, 100)
    sys.exit(app.exec_())

Solution

  • You need to filter events for the QGraphicsView that is a child widget of the GraphicsView class, because you only want mouse-moves on the graphics-view itself, not the whole container widget. So I would suggest something like this:

    The qGraphicView.py module:

    class GraphicsView(QtGui.QWidget):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.graphicsView = QtGui.QGraphicsView(self)
            self.graphicsView.setMouseTracking(True)
            self.graphicsLabel = QtGui.QLabel("Graphics View within QWidget")
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.graphicsLabel)
            layout.addWidget(self.graphicsView)
    
        def viewport(self):
            return self.graphicsView.viewport()
    

    The qWidgetView.py module:

    class WidgetView(QtGui.QWidget):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.graphicsView = GraphicsView()
            self.graphicsView.viewport().installEventFilter(self)
            self.edit = QtGui.QLineEdit(self)
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.edit)
            layout.addWidget(self.graphicsView)