QQmlApplicationEngine获取根对象报错:列表索引越界求助
问题原因分析
你遇到的问题核心是QML的Component.onCompleted触发时机早于QQmlApplicationEngine将Window对象加入rootObjects()列表的时机:
- 当QML里的
Component.onCompleted执行时,虽然当前组件(Rectangle)已经初始化完成,但QQmlApplicationEngine还没来得及把Window实例注册到rootObjects集合中,这时候调用self.engine.rootObjects()[0]自然会因为列表为空抛出索引越界错误。 - 而通过MouseArea点击触发时,整个程序已经完成初始化流程,
rootObjects()里已经存在Window实例,所以调用能正常执行。
解决方案:直接传递Window对象到Python槽函数
最可靠的方式是跳过rootObjects()的依赖,直接在QML中把Window实例传递给Python的槽函数,彻底避免时机匹配问题。具体修改如下:
1. 修改Python中的init_window槽函数
更新槽函数,让它接收一个QObject类型的参数(即QML的Window对象):
from PySide2.QtWidgets import QApplication from PySide2.QtQml import QQmlApplicationEngine from PySide2.QtCore import QObject, QUrl, Slot, QObject import sys import win32gui class Foo(QObject): def __init__(self): super().__init__() self.engine = QQmlApplicationEngine() self.window_attached = False # 用实例变量替代全局flag,更安全 @Slot(QObject) def init_window(self, window_obj): if not self.window_attached: # 直接使用传递过来的Window对象,无需从rootObjects获取 QmlObj = window_obj 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) self.window_attached = True if __name__ == "__main__": app = QApplication(sys.argv) foo = Foo() foo.engine.rootContext().setContextProperty("foo", foo) foo.engine.load(QUrl("main.qml")) if not foo.engine.rootObjects(): sys.exit(-1) sys.exit(app.exec_())
2. 修改QML中的调用逻辑
在Component.onCompleted和MouseArea.onClicked中,把Window实例直接传递给Python函数:
import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 2.0 Window { width: 200 height: 100 visible: true // 直接在Window的Component.onCompleted传递自身 Component.onCompleted: foo.init_window(this) Rectangle { anchors.fill: parent color: "red" MouseArea { anchors.fill: parent // 通过parent获取Window实例传递 onClicked: foo.init_window(parent) } Text { anchors.centerIn: parent text: "Hello, World!" } Button { text: "Ok" onClicked: { console.log("OK Button clicked....") } } } }
小提示:如果是在Window的直接子组件中调用,用
parent就能拿到Window实例;如果嵌套层级更深,可以用window关键字直接引用当前窗口,比如foo.init_window(window),更直观。
额外说明
这次修改不仅解决了时机问题,还把全局变量flag替换成了类的实例变量,避免了全局变量带来的潜在冲突,代码的封装性和安全性也更好。
内容的提问来源于stack exchange,提问作者lxchx
相关产品推荐
相关产品推荐

