为啥我不能使用我的 QML 单例模块?

Posted

技术标签:

【中文标题】为啥我不能使用我的 QML 单例模块?【英文标题】:Why can I not use my QML singleton module?为什么我不能使用我的 QML 单例模块? 【发布时间】:2021-03-05 18:50:19 【问题描述】:

我在https://github.com/jh3010-qt-questions/font_test有一个简单的测试项目

当我构建它时,我得到了错误:

qrc:/main.qml:5 模块“字体”未安装

我的目录结构如下:

font_test
├── assets
│   └── Fonts
│       ├── Art Brewery.ttf
│       ├── Fonts.qml
│       ├── Roboto-Light.ttf
│       ├── Roboto-Medium.ttf
│       ├── Roboto-Regular.ttf
│       └── qmldir
├── font_test.pro
├── font_test.pro.user
├── main.cpp
├── main.qml
└── qml.qrc

我的qml.qrc 文件是:

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
    </qresource>
    <qresource prefix="/Fonts">
        <file alias="Art Brewery.ttf">assets/Fonts/Art Brewery.ttf</file>
        <file alias="Roboto-Light.ttf">assets/Fonts/Roboto-Light.ttf</file>
        <file alias="Roboto-Medium.ttf">assets/Fonts/Roboto-Medium.ttf</file>
        <file alias="Roboto-Regular.ttf">assets/Fonts/Roboto-Regular.ttf</file>
    </qresource>
</RCC>

我的project file 是:

QT += quick

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = $$PWD/assets

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$$TARGET/bin
else: unix:!android: target.path = /opt/$$TARGET/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES += \
  assets/Fonts/Fonts.qml \
  assets/Fonts/qmldir

我的Fonts.qml 文件是:

pragma Singleton
import QtQuick 2.12

Item

  readonly property FontLoader artBrewery: FontLoader  source: "qrc:/Fonts/Art Brewery.ttf" 
  readonly property FontLoader robotoLight: FontLoader  source: "qrc:/Fonts/Roboto-Light.ttf" 
  readonly property FontLoader robotoMedium: FontLoader  source: "qrc:/Fonts/Roboto-Medium.ttf" 
  readonly property FontLoader robotoRegular: FontLoader  source: "qrc:/Fonts/Roboto-Regular.ttf" 

我在 assets/fonts 文件夹中的qmldir 是:

singleton Fonts 1.0 Fonts.qml

我尝试使用main.qml中的字体

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

import Fonts 1.0

Window 
  width: 640
  height: 480
  visible: true
  title: qsTr("Hello World")

  Column
  
    spacing: 8

    anchors.fill: parent
    anchors.margins: 8

    Label
    
      text: "DEFAULT: Pack my box with five dozen liquor jugs"
    

    Label
    
      text: "ART BREWERY: Pack my box with five dozen liquor jugs"

      font.family: Fonts.artBrewery.name
      font.pixelSize: 36
    

    Label
    
      text: "ROBOTO: Pack my box with five dozen liquor jugs"
    
  

在我将QML_IMPORT_PATH = $$PWD/assets 添加到我的项目文件之前,Qt Creator 会抱怨import Fonts 1.0。这样做有意义吗?

我想知道assets/fonts/qmldir 是否属于DISTFILES...?

我不确定需要更改什么,所以 main.qml 中的 font.family: Fonts.artBrewery.name 可以工作。

【问题讨论】:

文件夹名称和导入名称大小写不匹配,这是问题中的错误,还是实际情况如何?他们应该匹配 这不是问题中的错误,可以通过访问 GitHub 存储库来确认。我已经更正了,但问题仍然存在。还有其他事情发生。 【参考方案1】:

如here 所述,该模块必须在 QML 引擎的导入路径中可用:

要定义一个模块,开发者应该将模块中的各种 QML 文档、javascript 资源和 C++ 插件收集到一个目录中,并编写一个适当的模块定义 qmldir 文件,该文件也应该放入该目录中。然后可以将该目录作为模块安装到 QML 导入路径中。

您可以通过使用包含qmldir 的目录的父目录的路径调用addImportPath() 来执行此操作。当您使用

导入您的模块时
import Fonts 1.0

QML 引擎在每个导入路径中查找名为Fonts 的目录,如果找到,则查找qmldir 文件。由于您在 QRC 文件中使用了前缀,因此 qmldir 文件的最终路径是 :/Fonts/qmldirFonts的父目录(包含qmldir的目录)是根资源目录:/,所以:

diff --git a/main.cpp b/main.cpp
index 3d80569..de4efb7 100644
--- a/main.cpp
+++ b/main.cpp
@@ -10,6 +10,7 @@ int main(int argc, char *argv[])
   QGuiApplication app(argc, argv);
 
   QQmlApplicationEngine engine;
+  engine.addImportPath(":/");
   const QUrl url(QStringLiteral("qrc:/main.qml"));
   QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                    &app, [url](QObject *obj, const QUrl &objUrl) 

您还需要确保您的模块使用的所有文件都可用作资源。这意味着添加以下文件:

diff --git a/qml.qrc b/qml.qrc
index f212706..40cb8dc 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -3,6 +3,8 @@
         <file>main.qml</file>
     </qresource>
     <qresource prefix="/Fonts">
+        <file alias="qmldir">assets/Fonts/qmldir</file>
+        <file alias="Fonts.qml">assets/Fonts/Fonts.qml</file>
         <file alias="Art Brewery.ttf">assets/Fonts/Art Brewery.ttf</file>
         <file alias="Roboto-Light.ttf">assets/Fonts/Roboto-Light.ttf</file>
         <file alias="Roboto-Medium.ttf">assets/Fonts/Roboto-Medium.ttf</file>

【讨论】:

太棒了。这样可行。我很惊讶 QML_IMPORT_PATH 与 engine.addImportPath 的用途不同。我从 .pro 文件中删除了 QML_IMPORT_PATH 并从 .pro 文件中删除了 DISTFILES +=。 您是否尝试过使用QML2_IMPORT_PATH 我刚刚做了,而且效果也很好。我想我比一行 c++ 代码更喜欢这个解决方案。我想在其他因素影响只能由代码知道和确定的路径的情况下,c++ 解决方案会更好。 不! QML_IMPORT_PATH / QML2_IMPORT_PATH 用于 QtCreator。它使 QtCreator 识别模块,自动完成它们的内容,而不是把它们都标为错误。 QML_IMPORT_PATH 存在是因为 QtCreator 无法执行 C++ 代码来了解模块的位置。如果您不使用engine.addImportPath(),它只会在从 QtCreator 中启动时起作用,或者如果您从预先明确设置 QML2_IMPORT_PATH 的批处理脚本运行它。这是一个坏主意。如果您使用此方法,则运行时需要 engine.addImportPath() 和 QtCreator 需要 QML_IMPORT_PATH 哦,有趣。感谢您提供更多信息。【参考方案2】:

我看到另一个答案在几分钟内击败了我。那好吧。我会发布我的以防万一。

如何通过目录导入解决问题:

    将丢失的文件添加到qml.qrc文件中的资源列表:

    <qresource prefix="/Fonts">
     ...........
      <file alias="Fonts.qml">assets/Fonts/Fonts.qml</file>
      <file alias="qmldir">assets/Fonts/qmldir</file>
    

请注意aliases,否则路径会不同

如果您不将文件添加到资源中,它们将不会通过qrc:// 可见,并且它们也不会包含在您编译的应用程序中,即您的代码只能在您的计算机上运行,​​除非您安装丢失的文件手动使用您的应用程序

    main.qml移除模块导入:

    //import Fonts 1.0
    

如果您通过 qmldir 文件创建单例,则不需要(并且不会工作)

导入将不起作用,因为 Qt 不会在 qrc:// 路径中查找模块,除非您在 C++ 代码中明确指定 - 请参阅其他答案以了解如何执行此操作

    而是在main.qml 的顶部添加一个directory import:

    import "qrc:/Fonts/"
    

这会导入 qmldir file,它会根据您的指定从文件 Fonts.qml 创建一个名为 Fonts 的单例

请注意,虽然它在目录中导入了qmldir*.qml 文件,但它并没有告诉Qt 加载模块 来自同一个目录。

    我还必须将QT += qml 添加到font_test.pro,但如果您的 Qt/QML 版本完全可以编译,则可能不需要它

    像你已经尝试的那样使用你的单例:Fonts.artBrewery.name

参考文献

更多信息请参见 Qt 文档:

QML directory import 包括directory definition qmldir files module definition qmldir files

【讨论】:

+1 用于提及目录导入。也许您想链接到doc.qt.io/qt-5/qtqml-syntax-directoryimports.html 以获取更多信息。 谢谢。在答案末尾添加了参考。 太棒了。这样可行。我很惊讶 QML_IMPORT_PATH 与 engine.addImportPath 的用途不同。我从 .pro 文件中删除了 QML_IMPORT_PATH ,还从 .pro 文件中删除了 DISTFILES += 。奇怪的是,放置 import Fonts 1.0 确实允许它工作,但 Qt Creator 抱怨找不到模块。我猜这是一个错误。使用 import "qrc:/Fonts/" 也可以,并且是正确的解决方案。 就我而言,我认为 .pro 文件中的QML2_IMPORT_PATH 是更好的解决方案。 doc.qt.io/qt-5/qtqml-syntax-imports.html 的文档也与此问题相关

以上是关于为啥我不能使用我的 QML 单例模块?的主要内容,如果未能解决你的问题,请参考以下文章

C/C++为啥日志模块要设计成单例模式的?有啥好处?

为啥我不能在类方法中使用 python 模块 concurrent.futures?

QML 导入模块

PYTHON 为啥我的 python IDLE 不能将 numpy 识别为模块,尽管在 cmd 中运行 pythob 时可以正常使用 numpy?

尝试将外部 QML 模块导入我的项目时出错

为啥我不能在 Django 项目中导入模块?