PDA

View Full Version : Qt import library - creating a QML function



teh_raab
15th May 2015, 16:25
Hi All,

Im probably being really dumb and staring right at the issue but can't see the solution.. I've written an import library that we use for various Qt 4.8 QML applications. I have various objects that i can instantiate inside QML without any problem e.g.



Logger {
id: write;
name: "Main.qml";
Component.onCompleted: {
log("log started");
}
}


and as expected, have access to this "Logger" components "log" method e.g.



MouseArea {
anchors.fill: parent;
onClicked: {
write.log("Mouse area clicked)
console.log(convert.time_ms_to_sec(-1));
console.log(convert.time_ms_to_sec(15000));
}
}


This all works fine. My problem is that i now want to create some global functions inside my import library that can be called from anywhere without having to instantiate an object in every QML file. For example, i made a test function that converts milliseconds to seconds, but in order to use it i have to declare my "Conversion" object in each QML file, like..



Conversion {
id: convert;
}

MouseArea {
anchors.fill: parent;
onClicked: {
console.log(convert.time_ms_to_sec(15000));
}
}


Is there a way of doing this without having to declare the "Conversion {}"?!? I want to be able to simply call convert.time_ms_to_sec(<val>) in any script that has my import.

Thanks in advanced,

Raab.

wysota
15th May 2015, 16:41
You can make your plugin provide attached properties to objects or you can make it expose a global object using QQmlExtensionPlugin::initializeEngine() (or QDeclarativeExtensionPlugin::initializeEngine() if using Qt4). The latter would work similar to Math or Array objects in JavaScript.

teh_raab
15th May 2015, 17:06
Thanks :D QDeclarativeExtensionPlugin sounds exactly like what i'm after. I'll give it a bash.

teh_raab
18th May 2015, 12:02
So i had another look at the code (had to run after i sent previous reply so only just got back to looking at it).. I was already using the QDeclarativeExtensionPlugin. I just don't seem to be able to pin down the syntax. Here is what i have so far.

qmldir


plugin testplugin lib


testplugin.pro


TEMPLATE = lib
CONFIG += qt plugin
QT += declarative

DESTDIR = lib
OBJECTS_DIR = tmp
MOC_DIR = tmp

HEADERS += conversions.h \
testplugin.h

SOURCES += conversions.cpp \
testplugin.cpp


conversions.h


#ifndef CONVERSIONS_H
#define CONVERSIONS_H

#include <QDeclarativeItem>

class Conversions : public QDeclarativeItem
{
Q_OBJECT

public:
Conversions(QDeclarativeItem *parent = 0);

Q_INVOKABLE int time_ms_to_sec(int val);
};
#endif


testplugin.h


#ifndef TESTPLUGIN_H
#define TESTPLUGIN_H

#include <QDeclarativeExtensionPlugin>

class TestPlugin : public QDeclarativeExtensionPlugin
{
Q_OBJECT
public:
void registerTypes(const char *uri);
};
#endif


conversions.cpp


#include "conversions.h"

Conversions::Conversions(QDeclarativeItem *parent)
: QDeclarativeItem(parent)
{
// need to disable this flag to draw inside a QDeclarativeItem
setFlag(QGraphicsItem::ItemHasNoContents, false);
}


int Conversions::time_ms_to_sec(int val = -1) {
if(val == -1) {
return -1;
}
else {
return (val / 1000);
}
}



testplugin.cpp


#include "testplugin.h"
#include "conversions.h"
#include <qdeclarative.h>

void TestPlugin::registerTypes(const char *uri)
{
qmlRegisterType<Conversions>(uri, 1, 0, "Conversions");
}

Q_EXPORT_PLUGIN2(testplugin, TestPlugin);



And here is the QML -> I run this QML directly from QtCreater viewer. I have placed comments on line that is failing which shows what im trying to achieve.

Main.qml


import QtQuick 1.0
import TestPlugin 1.0

Item {
width: 100; height: 100

Conversions {
id: convv
Component.onCompleted: {
console.log("Test 1: " + convv.time_ms_to_sec(1000)); //Works ok
console.log("Test 2: " + time_ms_to_sec(2000)); //Works ok
}
}

MouseArea {
anchors.fill: parent;
onClicked: {
console.log("Test 3: " + convv.time_ms_to_sec(3000)); //Works ok
console.log("Test 4: " + Conversions.time_ms_to_sec(4000)); //DOES NOT WORK
}
}
}



My final goal being..

Main.qml


import QtQuick 1.0
import TestPlugin 1.0

Item {
width: 100; height: 100

MouseArea {
anchors.fill: parent;
onClicked: {
console.log("Test 4: " + Conversions.time_ms_to_sec(4000)); //DOES NOT WORK
}
}
}


I have seen references to "setContextProperty" but not sure if this is required for what im after, or if so how to use it.

Thanks again,

Rob.

wysota
18th May 2015, 12:44
Hmm... I even told you what function you were to reimplement :)


class Conversions : public QObject {
Q_OBJECT
public:
Conversions(QObject *parent = 0) : QObject(parent) {}
public slots:
int time_ms_to_sec(int val = -1) {
return val == -1 ? -1 : (val / 1000);
}
};

void TestPlugin::initializeEngine(QDeclarativeEngine *engine, const char *url) {
engine->rootContext()->setContextProperty("Conversions", new Conversions(engine));
}

teh_raab
18th May 2015, 13:25
You Sir, are a legend :) It was pretty much what i had but could not work out the issue..

I had to change



engine->rootContext()->setContextProperty("Conversions", new Conversions(engine));


to be..



engine->rootContext()->setContextProperty("_conversions", new Conversions(engine));


I could then call from QML -> "_conversions.time_ms_to_sec(4000)". Im assuming thats because i already have "Conversions" as a registered type ("qmlRegisterType<Conversions>(uri, 1, 0, "Conversions");")!

Thanks once again.

wysota
18th May 2015, 14:46
The question is why you registered "Conversions". What is the point of doing that? How does your "Conversions" type differ from "Conversion"?

teh_raab
19th May 2015, 14:08
It shouldnt. That was just the original implementation that i wanted kept in until i got the above working. It has since been removed.