【问题标题】:QML findChild from a different componentQML findChild 来自不同的组件
【发布时间】:2020-06-21 09:17:00
【问题描述】:

目标: 我正在为基于 Matplotlib 的嵌套样本库编写 Gui 前端(如果你想看看,请pip install anesthetic)。

我将如何在 C++ 中实现它:我之前使用 QML 的经验是一个 C++ 程序,我没有进入 QML 寻找要渲染的画布,而是创建了一个 C++ 对象,在 QML 的类型系统中注册它,并让它像 QtQuick 控件小部件一样工作。据我所知,这是推荐的做事方式:所有渲染都在 QML 中完成,所有业务端逻辑都在 C++ 中。

最好的方法以及为什么我不能这样做:这种方法在这里行不通。 AFAIK,您只能使用 C++ 实现自定义 QML,我需要程序是纯 Python(以便其他人能够维护它)一些 JS 是可访问的,QML 很容易理解和编辑,所以我没有反对意见(C++ 很难拒绝)。

我的工作内容:我有一个我想要的工作实现。这一切都在一个文件中。所以,很自然地,我想将我正在绘制的画布拆分为一个单独的文件:figure.qml。麻烦的是,每当从单独的文件加载对象时,我似乎都找不到该名称的对象(下一步是使用Loader,因为Figure 非常笨重)。

我有一个以view.qml 为根的两个文件项目,以及Figure.qml 中的一个组件。 问题是,只有当我在view.qml 中而不是在Component.qml 中加载带有objectName: "component" 的东西时,它才有效。

那么,Pyside 中的 findChild 是如何处理另一个 .qml 文件中的 objectName 的?

MWE:

ma​​in.py

import sys
from pathlib import Path
from matplotlib_backend_qtquick.backend_qtquickagg import FigureCanvasQtQuickAgg
from matplotlib_backend_qtquick.qt_compat import QtGui, QtQml, QtCore


def main():
    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    displayBridge = DisplayBridge()
    context = engine.rootContext()                       
    qmlFile = Path(Path.cwd(), Path(__file__).parent, "view.qml")
    engine.load(QtCore.QUrl.fromLocalFile(str(qmlFile)))
    win = engine.rootObjects()[0]
    if win.findChild(QtCore.QObject, "figure"):
        print('success') # This fails
    app.exec_()

view.qml

import QtQuick.Controls 2.12
import QtQuick.Windows 2.12

ApplicationWindow{
   Figure {

   }
}

Figure.qml

import QtQuick.Controls 2.12 
import QtQuick 2.12

Component{
  Rectangle{ 
    objectName: "figure"
  }
}

【问题讨论】:

  • 不太清楚你在问什么。什么不起作用 - 删除 objectName、嵌套项目,以及其他什么?
  • 已更新。问题是我得到一个None 作为查找文件的结果。它实际上迫使我将Component 保留在view.qml 中,我想知道是否有一些我错过的语义。
  • 首先值得注意的是,QtQuick 不适用于“文件”,而是用于排列在树中的QML 项目。 QObject::findChild 在这棵树中查找一个名为 的项目,在 QML 中是 objectName 的同义词,所以这个属性总是强制性的(也许你错过了)。您可能会错过的另一件事是options,应该是Qt::FindChildrenRecursively。 “失去”孩子的另一种方法是间接加载它,即使用 Loader 或类似的东西。
  • @AlexPetrosyan 请提供minimal reproducible example
  • @AlexPetrosyan 1)组件是异步加载的,所以你找不到它,2)你不应该从 python 访问 QML 元素,因为它们只会产生这种类型的不便,3)你可以解释你的基本目标(更详细地解释我正在尝试从 QML 对画布进行一些 Python 端渲染)对我来说你有一个 XY 问题,4)提供一个 MRE 来帮助你。

标签: python qml pyside2


【解决方案1】:

Component 用于定义 QML 元素,它不实例化 它,因此您无法访问该对象。创建一个 Figure.qml 相当于创建一个 Component,你是在另一个 Component 中创建一个 Component。

解决办法是不使用组件:

Figure.qml

import QtQuick.Controls 2.12 
import QtQuick 2.12

Rectangle{ 
    objectName: "figure"
}

但不建议使用 objectName,例如,如果您创建多个组件,您将如何识别它是哪个组件? o 如果您在时间 T 之后创建对象,或者使用 Loader 或 Repeater,您将无法应用该逻辑。最好创建一个允许获取这些对象的 QObject:

from PySide2 import QtCore
import shiboken2


class ObjectManager(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._qobjects = []

    @property
    def qobjects(self):
        return self._qobjects

    @QtCore.Slot(QtCore.QObject)
    def add_qobject(self, obj):
        if obj is not None:
            obj.destroyed.connect(self._handle_destroyed)
            self.qobjects.append(obj)
        print(self.qobjects)

    def _handle_destroyed(self):
        self._qobjects = [o for o in self.qobjects if shiboken2.isValid(o)]
# ...
object_manager = ObjectManager()
context = engine.rootContext()
context.setContextProperty("object_manager", object_manager)
qmlFile = Path(Path.cwd(), Path(__file__).parent, "view.qml")
engine.load(QtCore.QUrl.fromLocalFile(str(qmlFile)))
# ...
import QtQuick.Controls 2.12 
import QtQuick 2.12

Rectangle{ 
    Component.onCompleted: object_manager.add_qobject(this)
}

【讨论】:

  • @AlexPetrosyan mmm,我不懂你,你自己解释清楚
  • 我没有看到更新的答案。我想知道如何使用加载器,并且仍然能够获得画布......我很高兴有一种方法,我很高兴它不需要我记住名字并保持一切都在顶层。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-13
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
相关资源
最近更新 更多