QML Python中的自定义对象引用

3

我试图使用qmlRegisterType扩展QML。我有一个python类 - PyQml.pymain.qml文件以及与之相关的样板代码。

问题在于我无法在main.qml文件中引用(导入)PyQml对象,我得到一个错误 --> QML模块未找到(PyQml)

到目前为止,我已确定了QML_IMPORT_PATH变量路径。由于我很绝望,我在其中一个路径下创建了名为PyQml的文件夹,并将PyQml.py放入其中,但仍然没有成功。此外,在Qt Creator项目中,我找不到*.pro文件。我想我应该将另一个路径添加到自定义对象中。

PyQml.py

class PyQml(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        # Initialise the value of the properties.
        self._name = ''
        self._shoeSize = 0

    # Define the getter of the 'name' property.  The C++ type of the
    # property is QString which Python will convert to and from a string.
    @Property('str')
    def name(self):
        return self._name

    # Define the setter of the 'name' property.
    @name.setter
    def name(self, name):
        self._name = name

    # Define the getter of the 'shoeSize' property.  The C++ type and
    # Python type of the property is int.
    @Property(int)
    def shoeSize(self):
        return self._shoeSize

    # Define the setter of the 'shoeSize' property.
    @shoeSize.setter
    def shoeSize(self, shoeSize):
        self._shoeSize = shoeSize

qmlengine.py

import sys
import sqlite3
from PySide2 import QtCore, QtGui, QtWidgets, QtQuick
from PySide2.QtCore import Qt,QUrl
from PySide2.QtQml import QQmlApplicationEngine,qmlRegisterType
from PySide2.QtGui import QGuiApplication
from ViewModel import PyQml

if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    print(QQmlApplicationEngine.importPathList(engine))
    ctx = engine.rootContext()
    ctx.setContextProperty("qmlapp", engine) #the string can be anything
    qmlRegisterType(PyQml.PyQml, 'People', 1, 0, 'Person');
    engine.load('Documents/ctmd/Qml/main.qml')
    win = engine.rootObjects()[0]
    win.show()
    sys.exit(app.exec_())

main.qml

import QtQuick 2.0
import QtQuick.Controls 1.4
import PyQml 1.0 ---- Error QML module not found ( PyQml)
ApplicationWindow {

    menuBar: MenuBar {
        Menu {
            title: "File"
            MenuItem { text: "Open..." }
            MenuItem { text: "Close" }
        }

        Menu {
            title: "Edit"
            MenuItem { text: "Cut" }
            MenuItem { text: "Copy" }
            MenuItem { text: "Paste" }
        }
    }
    Grid {
        columns: 3
        spacing: 2
        Rectangle { color: "red"; width: 50; height: 50 }
        Rectangle { color: "green"; width: 20; height: 50 }
        Rectangle { color: "blue"; width: 50; height: 20 }
        Rectangle { color: "cyan"; width: 50; height: 50 }
        Rectangle { color: "magenta"; width: 10; height: 10 }
    }

}

项目文件夹树

Qml
   -main.qml
PyQml.py
qmlengine.py

PyQml只是一个示例类,在一天的结束时,我想通过qml传递我在python中计算的自定义数据(x,y坐标),并用qml绘制这些数据。


对我来说,当调用setter时无法更新屏幕上的getter,有什么问题吗? - kei nagae
2个回答

3

简介; 由于QtCreator和Qt for Python的限制,无法消除错误消息,因此没有解决方案。但是,QtCreator只是一个IDE,因此不需要在其中工作,相反,您只需要从控制台/CMD运行代码即可:

python /path/of/script.py

您有以下错误:
  • 当您使用qmlRegisterType向QObject注册“People”时,“People”是QML中的包名称,“Person”是组件名称,因此除非更改注册表参数,否则不应在导入中使用PyQml。

  • QtCreator/QtQuickDesigner对Python支持仍然存在限制,因此“Qml模块未找到(FooPackage)”的消息就是一个例子。如Qt for Python/PySide2的开发人员指出,在将来的版本中他们将添加新功能,但目前还无法实现。

  • 我看到您在发布中指定的结构与您的项目不匹配,例如,您指示主要的.qml位于与qmlengine.py同级的QML文件夹中,但在加载中却使用“Documents/ctmd/Qml/main.qml”。

  • PySide2在Property修饰符及其setter方面存在限制,因为它不被QML识别,而是使用广泛声明:name_of_property = Property(type_of_property, fget=getter_of_property, ...)

  • 如果一个具有setter的Qt属性,则必须有一个关联的信号。

考虑上述问题,解决方案如下:
├── PyQml.py
├── Qml
│   └── main.qml
└── qmlengine.py

qmlengine.py

import os
import sys

from PySide2.QtCore import QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType

import PyQml

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    qmlRegisterType(PyQml.PyQml, "People", 1, 0, "Person")
    engine = QQmlApplicationEngine()

    ctx = engine.rootContext()
    ctx.setContextProperty("qmlapp", engine)  # the string can be anything

    current_dir = os.path.dirname(os.path.realpath(__file__))
    filename = os.path.join(current_dir, "Qml/main.qml")

    engine.load(QUrl.fromLocalFile(filename))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

PyQml.py

from PySide2.QtCore import Property, Signal, QObject


class PyQml(QObject):
    nameChanged = Signal(str)
    shoeSizeChanged = Signal(int)

    def __init__(self, parent=None):
        super().__init__(parent)
        # Initialise the value of the properties.
        self._name = ""
        self._shoeSize = 0

    # Define the getter of the 'name' property.  The C++ type of the
    # property is QString which Python will convert to and from a string.
    def get_name(self):
        return self._name

    # Define the setter of the 'name' property.
    def set_name(self, name):
        if self._name != name:
            self._name = name
            self.nameChanged.emit(name)

    name = Property(str, fget=get_name, fset=set_name, notify=nameChanged)

    # Define the getter of the 'shoeSize' property.  The C++ type and
    # Python type of the property is int.
    def get_shoeSize(self):
        return self._shoeSize

    # Define the setter of the 'shoeSize' property.
    def set_shoeSize(self, shoeSize):
        if self._shoeSize != shoeSize:
            self._shoeSize = shoeSize
            self.shoeSizeChanged.emit(shoeSize)

    shoeSize = Property(
        int, fget=get_shoeSize, fset=set_shoeSize, notify=shoeSizeChanged
    )

main.qml

import QtQuick 2.0
import QtQuick.Controls 1.4
import People 1.0

ApplicationWindow {
    visible: true

    Person{
        name: "foo"
        shoeSize: 10
    }

    menuBar: MenuBar {
        Menu {
            title: "File"
            MenuItem { text: "Open..." }
            MenuItem { text: "Close" }
        }

        Menu {
            title: "Edit"
            MenuItem { text: "Cut" }
            MenuItem { text: "Copy" }
            MenuItem { text: "Paste" }
        }
    }
    Grid {
        columns: 3
        spacing: 2
        Rectangle { color: "red"; width: 50; height: 50 }
        Rectangle { color: "green"; width: 20; height: 50 }
        Rectangle { color: "blue"; width: 50; height: 20 }
        Rectangle { color: "cyan"; width: 50; height: 50 }
        Rectangle { color: "magenta"; width: 10; height: 10 }
    }
}

感谢您的及时回复。我收到了相同的错误信息:“QML模块未找到(People)”。当鼠标悬停在上面时,我会得到额外的信息:“导入路径:C:/Qt/5.12.3/msvc2017_64/qml 对于qmake项目,请使用QML_IMPORT_PATH变量添加导入路径。对于Qbs项目,请在产品中声明并设置qmlImportPaths属性以添加导入路径。对于qmlproject项目,请使用importPaths属性添加导入路径。对于Cmake项目,请确保CMakeCache.txt中有QML_IMPORT_PATH变量。 - user2727167
@user2727167 请注意:QtCreator/QtQuickDesigner在Python支持方面仍存在限制,因此出现“Qml模块未找到(FooPackage)”的消息是其局限性的一个示例。正如Qt for Python/PySide2的开发人员所指出的,在未来的版本中,他们将添加新功能,但目前还不可能实现。 - eyllanesc
@user2727167 看起来你没有看到我的回答,QtCreator目前在使用Python和特别是在QML中使用Python方面存在一些限制,但是Qt Creator只是一个IDE,所以不是必须所有东西都在那里工作。可以从控制台/CMD中运行测试:python /path/of/qmlengine.py - eyllanesc
为了后人留存:在PySide2中,属性装饰器存在问题的原因在这里得到了解释。 https://bugreports.qt.io/browse/PYSIDE-900 - MC-8

0

我发现如果我简单地忽略这个错误

在 QML 窗口中一切都正常工作。我甚至尝试重新安装 IDE,但错误仍然存在。感谢您的澄清。


对我来说,这个错误不能被忽视。当我尝试打开QML表单编辑器时,会出现以下错误(见下图),然后点击“确定”会将我带回到QML文本编辑器。因此,每次我想使用所见即所得的QML编辑器时,我都必须在QML代码中注释导入语句。`行:7:找不到QML模块(<module>)。导入路径: <path>对于qmake项目,请使用QML_IMPORT_PATH变量添加导入路径。 对于Qbs项目,请在产品中声明并设置qmlImportPaths属性以添加导入路径。 [...]` - yzokras

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接