James Hudson
James Hudson

Reputation: 904

Why can I not use my QML singleton module?

I have a simple test project at https://github.com/jh3010-qt-questions/font_test

When I build it, I get the error:

qrc:/main.qml:5 module "Fonts" is not installed

My directory structure looks like:

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

my qml.qrc file is:

<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>

my project file is:

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

My Fonts.qml file is:

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" }
}

My qmldir inside of the assets/fonts folder is:

singleton Fonts 1.0 Fonts.qml

I try to use the font in 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"
    }
  }
}

Before I added QML_IMPORT_PATH = $$PWD/assets to my project file, Qt Creator would complain about import Fonts 1.0. Does it make sense this would be required?

I am wondering if assets/fonts/qmldir belongs in DISTFILES...?

I am not sure what needs to be changed so font.family: Fonts.artBrewery.name in main.qml will work.

Upvotes: 1

Views: 2526

Answers (2)

Jack White
Jack White

Reputation: 916

I see another answer beat me in a couple of minutes. Oh well. I'll post mine just in case.

How to fix the problem through directory import:

  1. Add missing files to resource list in qml.qrc file:

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

please note the aliases, otherwise paths will be different

If you do not add the files to resources, they won't be visible through qrc://, and also they won't be included into your compiled application i.e. your code will only run on your computer unless you install missing files manually with your application

  1. Remove module import from main.qml:

    //import Fonts 1.0
    

This is not needed (and will not work) if you create your singleton through qmldir file

The import won't work because Qt won't be looking for modules within qrc:// path unless you explicitly specify so in C++ code - see the other answer on how to do that

  1. Instead add a directory import to the top of main.qml:

    import "qrc:/Fonts/"
    

this imports qmldir file which creates a singleton named Fonts from file Fonts.qml as you specified

Please note that although it imports qmldir and *.qml files inside the directory, it does not tell Qt to load modules from that same directory.

  1. I also had to add QT += qml to font_test.pro but it's probably not needed for your version of Qt/QML if it compiles at all

  2. Use your singleton like you are already trying to do: Fonts.artBrewery.name

References

See Qt documentation for more information:

Upvotes: 2

Mitch
Mitch

Reputation: 24406

As described here, the module has to be available in the QML engine's import path:

To define a module, a developer should gather together the various QML documents, JavaScript resources and C++ plugins which belong in the module into a single directory, and write an appropriate module definition qmldir file which should also be placed into the directory. The directory can then be installed into the QML import path as a module.

You can do this by calling addImportPath() with the path to the parent directory of the directory that contains the qmldir. When you import your module with

import Fonts 1.0

the QML engine looks through each import path for a directory called Fonts, and if it finds it, looks for a qmldir file. Since you use a prefix in your QRC file, the final path to the qmldir file is :/Fonts/qmldir. The parent directory of Fonts (the directory that contains the qmldir) is the root resource directory :/, so:

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) {

You also need to ensure that all of the files your module uses are available as resources. That means adding the following files:

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>

Upvotes: 4

Related Questions