pythonqtqmlpyside2qqmlapplicationengine

Error in getting the root object from QQmlApplicationEngine in Component.onCompleted


I try to get the root object after window having completed, but I get a error:

QmlObj = self.engine.rootObjects()[0]

Error: list index out of range

The strange thing is that it works when I try to call foo.init_window() after the MouseArea having clicked.

Here is my python code:

main.py

from PySide2.QtWidgets import QApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, QUrl, Slot

import sys
import win32gui

flag = False


class Foo(QObject):
    def __init__(self):
        super().__init__()
        self.engine = QQmlApplicationEngine()

    @Slot()
    def init_window(self):
        global flag
        if not flag:
            QmlObj = self.engine.rootObjects()[0]
            desk = win32gui.FindWindow("Progman", "Program Manager")
            print(desk)
            sndWnd = win32gui.FindWindowEx(desk, 0, "SHELLDLL_DefView", None)
            print(sndWnd)
            targetWnd = win32gui.FindWindowEx(sndWnd,
                                              0, "SysListView32", "FolderView")
            print(targetWnd)
            win32gui.SetParent((int)(QmlObj.winId()), targetWnd)
            flag = True


if __name__ == "__main__":
    app = QApplication(sys.argv)
    foo = Foo()

    foo.engine.rootContext().setContextProperty("foo", foo)
    foo.engine.load(QUrl("main.qml"))
    # win = foo.engine.rootObjects()[0]
    # win.show()

    if not foo.engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

Here is the .qml file:

main.qml

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.0

Window {
    width: 200
    height: 100
    visible: true
    //flags: Qt.FramelessWindowHint
    //flags: Qt.WindowStaysOnBottomHint
    //flags: Qt.WindowMinMaxButtonsHint

    Rectangle {
        anchors.fill: parent
        color: "red"

        Component.onCompleted: foo.init_window()

        MouseArea {
            anchors.fill: parent
            onClicked: foo.init_window()
        }

        Text {
            anchors.centerIn: parent
            text: "Hello, World!"
        }

        Button {
            text: "Ok"
            onClicked: {
                console.log("OK Button clicked....")
            }
        }
    }
}

Solution

  • The problem is that in Component.onCompleted the window(the rootObject) has finished building but the engine list has not been updated. The solution is to invoke init_window an instant later using Qt.callLater():

    Component.onCompleted: Qt.callLater(foo.init_window)