Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to put Qt QML non-visual application components

Tags:

qt

qt-quick

qml

(I'm using Qt 5, QtQuick 2, with Controls.)

I'm writing my first QML application and would like to maintain modularity and relatively small file lengths.

There are certain things that come in big batches that I would like to tuck away into helper files, but I'm not sure of the right way to do this.

The two examples I've run into so far have been FontLoader and Action.

In my particular situation, I'll be using 6 fonts in my whole application, so I would like to load them all early on, and not have to worry about it again. My question is, where should I put the FontLoaders?

One option is to just put them in main.qml somewhere, but that feels a bit cluttered to me. What I've done is created a file FontLoaders.qml, which contains:

Item {
    width: 0; height: 0
    FontLoader {
        name: "myFont1"
        source: "/fonts/myFont1.ttf"
    }
    FontLoader {
        name: "myFont2"
        source: "/fonts/myFont2.ttf"
    }
    // ...
}

Then, in main.qml somewhere, I just have a FontLoaders {}.

The other example I have is Action. I have a big batch of Actions that I'd like to be visible more-or-less application-wide. I have a AppMenuBar component, and this seems like a natural place to put the Actions, but that basically doubles the size of the QML file defining the menu bar, and logically speaking the two could be split out. (Many of the actions are accessible both through the menu bar and through other buttons in the application.)

The "solutions" I have basically work, but they feel sloppy to me (especially FontLoaders), and I'm wondering if there is some cleaner way to do this that I haven't figured out yet.

like image 413
Andrey Mishchenko Avatar asked Dec 29 '14 16:12

Andrey Mishchenko


2 Answers

I have similar issues. I did have a look at some of the sources of Qt and figured you can do this:

pragma Singleton

import QtQuick 2.0
QtObject {

    property FontLoader fontLatoBlackLoader: FontLoader {
        name: "fontLatoBlack"
        source: "qrc:/fonts/Lato-Black.ttf"
    }
}

In your say fonts.qml file and then in the application you just do:

Label {
    text: "Sample tesxt"
    font.family: Fonts.fontLatoBlackLoader.name
}

You don't have to instantiate the Fonts object. It just exists globally.

Hope that helps. I have spent a few hours to figure this out myself as I had the same sloppiness/ugliness issues :)

like image 74
ervinbosenbacher Avatar answered Oct 07 '22 21:10

ervinbosenbacher


I'd like to be visible more-or-less application-wide

There are several ways you can go about:

1 - QML has dynamic scoping, which means if you declare some property in the root component, it will be accessible everywhere, as long as it is not shadowed by an identically named local property, you could have a property var something : someObject and use it anywhere. It doesn't even need to be a property, if you set an id FontLoaders { id: fontLoaders } you can use fontLoaders everywhere.

2 - you can use a shared library and use a var in there and include the library everywhere you need that object available:

// Test.js
.pragma library
var obj

// in qml
import "Test.js" as Shared
Shared.obj = someObject      

3 - you can find the object in C++ once the main QML is instantiated and register it as a context property, making it globally available in QML

4 - you can use a C++ class to create a wrapper to server as an accessor to shared data

3 and 4 are explained with code snippets in this answer, although this is more useful for C++ objects you use in QML, frankly, the dynamic scoping in QML is the easiest way to go.

As of where exactly to put them - wherever it makes most sense. If you need those application-wide, the main component seems the obvious choice, and if you don't want to clutter it too much, declare those in separate qml files and just instantiate in the main as you've done with the font loaders.

like image 22
dtech Avatar answered Oct 07 '22 21:10

dtech