Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to connect PyQt5 pyqtSlot to QML ListView signal "activated"?

I would like to connect the activated signal from a QML ListView to a pyqtSlot decorated method from my Python3/PyQt5 (5.6) code.

My current approach is to load the QML scene in my code through QQmlApplicationEngine and then use findChild() to get a reference to my ListView.

The problem is, that I can only find the ListView when searching for a QObject like findChild(QObject, 'myList'). But htis object does not give me access to the activated signal, most likely because this signal is only defined for QAbstractItemView and it descendants.

So if I try findChild(QListView, 'myList') the result is None. Therefor I'm not able to get to the activated signal. Is this a bug in PyQt5 or is there another way for me to connect to this signal?

Here is some minimal working example.

list.py:

import sys
from OpenGL import GL
from PyQt5.QtCore import QUrl, QObject
from PyQt5.QtWidgets import QApplication, QListView
from PyQt5.QtQml import QQmlApplicationEngine

# Main Function
if __name__ == '__main__':
    # Create main app
    app = QApplication(sys.argv)

    # Create QML engine
    engine = QQmlApplicationEngine(app)

    # Load the QML scene from file
    engine.load(QUrl('List.qml'))

    for root in engine.rootObjects():
        node = root.findChild(QListView, 'myList')
        if node:
            # At this point I would like to connect something to the
            # node.activated signal
            print(node)

    # Execute the application and exit
    sys.exit(app.exec_())

List.qml:

import QtQuick 2.0
import QtQuick.Window 2.2

Window {
  visibility: Window.FullScreen
  visible: true
  ListView {
    objectName: "myList"
    anchors.fill: parent
    delegate: Item {
      width: parent.width * 0.8
      height: 40
      Row {
        id: row1
        Rectangle {
          width: 40
          height: 40
          color: colorCode
        }

        Text {
          text: name
          font.bold: true
          anchors.verticalCenter: parent.verticalCenter
        }
        spacing: 10
      }
    }
    model: ListModel {
      ListElement {
        name: "Grey"
        colorCode: "grey"
      }

      ListElement {
        name: "Red"
        colorCode: "red"
      }

      ListElement {
        name: "Blue"
        colorCode: "blue"
      }

      ListElement {
        name: "Green"
        colorCode: "green"
      }
    }
  }

}
like image 644
Fladi Avatar asked Jul 14 '16 06:07

Fladi


1 Answers

You can do that by using QQuickView instead of QQmlApplicationEngine.

I changed your python script to add a new class which inherits from QQuickView, and added a signal to the QML object named "myList".

Moreover, into the QML I removed the Window type for Item type (you can't use Window with QQuickView). If you want to display your application in full screen, you'll have to specify it into MyView class. In the example, if you click on one of the colored rectangles, the index will be displayed in the console.

list.py:

import sys
from PyQt5.QtCore import QUrl, QObject
from PyQt5.QtWidgets import QApplication, QListView
from PyQt5.QtQuick import QQuickView, QQuickItem

class MyView(QQuickView):
    def __init__(self, parent=None):
        super().__init__(parent)
        # Load the QML scene from file
        self.setSource(QUrl('List.qml'))    
        #connect signal and source
        list = self.rootObject().findChild(QQuickItem, 'myList')
        list.mySignal.connect(self.mySlot)

    def mySlot(self, index):
        print(index)

# Main Function
if __name__ == '__main__':
    # Create main app
    app = QApplication(sys.argv)

    # Create QML view
    view = MyView()
    view.show()    

    # Execute the application and exit
    sys.exit(app.exec_())

List.qml:

import QtQuick 2.0
import QtQuick.Window 2.2

Item {
  width: 500
  height: 500
  ListView {
    anchors.fill: parent
    id: list
    objectName: "myList"
    signal mySignal(int index)
    delegate: Item {
      width: parent.width * 0.8
      height: 40
      Row {
        id: row1
        Rectangle {
          width: 40
          height: 40
          color: colorCode

          MouseArea{
            anchors.fill: parent
            onClicked: list.mySignal(index)
          }
        }

        Text {
          text: name
          font.bold: true
          anchors.verticalCenter: parent.verticalCenter
        }
        spacing: 10
      }
    }
    model: ListModel {
      ListElement {
        name: "Grey"
        colorCode: "grey"
      }

      ListElement {
        name: "Red"
        colorCode: "red"
      }

      ListElement {
        name: "Blue"
        colorCode: "blue"
      }

      ListElement {
        name: "Green"
        colorCode: "green"
      }
    }
  }

}
like image 142
lolo Avatar answered Sep 30 '22 09:09

lolo