Setting context property for multiple qml files - qt

I have a main.qml and inside that i have two banners(titlebanner and bottombanner) as shown in below.
Window {
id: main
visible: true
Image {
source: "qrc:/banner.png"
anchors.fill: parent
}
TitleBanner {
x:0
y:10
}
BottomBanner {
x:0
y:420
}
}
In TitleBanner.qml, i have below code
Item {
Rectangle {
id : id_title_banner
property real value : titlebanner.title
Image {
source: "qrc:/banner_title.png";
}
//todo:: some animation stuffs on 'value' later
}
}
In BottomBanner.qml, i have below code
Item {
Rectangle {
id : id_bottom_banner
property real value : bottombanner.title
Image {
source: "qrc:/banner_bottom.png";
}
//todo:: some animation stuffs on 'value' later
}
}
In C++ side, I am keeping two separate objects(for later flexibility) for both title banner and bottom banner. I set the root context property to expose the title banner objects from C++ to qml as below
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//create title banner object
CTitleBanner *objTitleBanner = new CTitleBanner();
//create bottom banner object
CBottomBanner *objBottomBanner = new CBottomBanner();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("titlebanner", objTitleBanner);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
My question is, how can we set context property for both titlebanner and bottombanner qml files separately instead of setting it to root context ? My concern is at later point if more banner comes, I set it to rootContext. Is it a proper way of doing ? How can we create seperate context for each banner ?

Your assumption:
it replaces above context property.
is wrong:
// in main.cpp
QObject* obj1 = new QObject();
QObject* obj2 = new QObject();
engine.rootContext()->setContextProperty("obj1", obj1);
engine.rootContext()->setContextProperty("obj2", obj2);
qDebug() << "obj1" << obj1 << engine.rootContext()->contextProperty("obj1").value<QObject*>();
qDebug() << "obj2" << obj2 << engine.rootContext()->contextProperty("obj2").value<QObject*>();
// in main.qml
Component.onCompleted: console.log(obj1, obj2)
// output:
obj1 QObject(0x22d8c768) QObject(0x22d8c768)
obj2 QObject(0x22d8c778) QObject(0x22d8c778)
qml: QObject(0x22d8c768) QObject(0x22d8c778)
Both objects are there, with the right identifier.
Personally I don't like contextProperties since they are easily to be shadowed e.t.c. - I rather register my CPP models as singletons so I can import them at the places where I really want to have access to them. Though there are downsides of those singletons. (e.g. qmlscene will fail to import them)

Related

Qt: Track mouse position while QDrag is running

I am developing a Qt application with multiple windows and want to implement cross-window drag&drop functionality for some elements in my program.
To do so, I attach an event filter to the to-be-dragged QML elements and listen for the MousePress/MouseMove events to start the drag procedure as follows:
QDrag *drag = new QDrag(quickItem);
QMimeData* mimeData = new QMimeData();
mimeData->setText("Test");
drag->setHotSpot(QPoint(0, 0));
drag->setMimeData(mimeData);
drag->exec();
This works fine, but now I would like to show a little tooltip (being a QWidget) while dragging, following the mouse cursor and displaying a short text depending on the element the mouse is currently over (similar to the "Copy to ..." or "Move to..." labels appearing when you drag files around in Windows Explorer).
However, while dragging the element, I don't receive any MouseMove events neither on the QDrag object nor on the quickItem itself, which makes it impossible to track the mouse position. Since the mouse is grabbed during dragging, there should be some event in Qt that frequently reports the mouse position, no matter where on the screen the mouse is.
I am aware of the QDrag::setPixmap method, however this won't allow me to change my tooltip text during dragging and has some other limitations I would like to avoid.
Is there some way to listen to mouse move events while QDrag is running, without using platform-specific system APIs?
Update:
I don't think that there is way of doing this without using OS libraries nor getting the mouse position every X miliseconds. It looks like a really specific problem that Qt framework does not contemplate. You will need to write your own class to control this using win32 for windows, x11 for linux and the equivalent of Mac.
If you want to get the mouse position when your window is active and you are dragging something, check this:
Searching a bit I've found a solution for getting it when your window has the focus using QObject::eventFilter.
Create a class (for example EventListener) that inherits from QObject and overrides eventFilter and a method to set this as your qml window (which inherits from QObject) event filter with installEventFilter.
eventslistener.h:
#include <QEvent>
#include <QObject>
#include <QDebug>
#include <QDropEvent>
class EventsListener : public QObject
{
Q_OBJECT
public:
EventsListener(QObject * ptr) : QObject (ptr) {
}
Q_INVOKABLE void handleEventsOf(QObject *object) {
if (object)
object->installEventFilter(this);
}
bool eventFilter(QObject *object, QEvent *event) override {
if(event->type() == QEvent::DragMove) {
QDragMoveEvent *mouseEvent = static_cast<QDragMoveEvent*>(event);
qDebug() << "Mouse position dragging (x, y): (" << mouseEvent->pos().x() << ", " << mouseEvent->pos().y() << ")";
return false; //this is must return false or drop event will be handled by this method and drag&drop won't work correctly
}
return false;
}
};
Now we need to access to an instance (singleton in this case) of this class with qmlRegisterSingletonType. You may wish to use qmlRegisterType instead to register this eventlistener as a type (instead of a singleton) and use signals to notify directly qml the mouse position.
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "eventlistener.h"
static QObject *eventsListenerInstance(QQmlEngine *qmlEngine, QJSEngine *engine)
{
return new EventsListener(engine);
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<EventsListener>("AppEventListener", 1, 0, "EventsListener", eventsListenerInstance);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import ...
import AppEventListener 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
id: item
property string display
property alias dropEnabled: acceptDropCB.checked
color: dropArea.containsDrag ? "#CFC" : "#EEE"
Component.onCompleted: EventsListener.handleEventsOf(item)
...
}

QML - How to use QStringList as a model of ListView?

I try to make TelnetClient.I use FileIO for read Telnet.There is no problem read or write and also create a string list but I need to show QStringList to ListView but I m getting error: "m_model is not defined".
I create QStringList:
QStringList FileIO::read() {
if (m_source.isEmpty()) {
emit error("source is empty");
return QStringList();
}
QFile file(m_source);
QString fileContent;
QString line;
QStringList list;
if ( file.open(QIODevice::ReadWrite) ) {
QTextStream t( &file );
line = t.readAll();
fileContent += line;
list.append(line.split("\r\n"));
foreach (QString item, list) {
if (item[0].isNumber()) {
list2.append(item);
}
}
QQmlContext *ctxt;
ctxt->setContextProperty("m_model", QVariant::fromValue(list2));
qDebug() << "\r\n\r\nlist2 =" << list2;
line = t.readAll();
qDebug() << "SOURCE" << m_source;
file.close();
}
else {
emit error("Unable to open the file");
return QStringList();
}
return list2;
This can make a new QStringList successfully and also I assign my string list as a model; m_model.
ListView {
id: listView1
x: 0
y: 0
model: m_model
delegate: Rectangle{
Text {text: modelData }
}
}
and here is my ListView. When I try like this, I m getting error. How can I solve this problem. If I can use "list2" in main.cpp I can solve the problem but I don't know how can I use it in main.cpp because it exist in another class.
Thank you!
You can try to set the context property with an instance of the class. That way, you can instantiate the class in main, and then pass it's address to set the context property. If the data of the model is subject to change while the program is running, I would suggest implementing the QStringList as a Q_Property.
//main.cpp
FileIO fileIO;
QQmlApplicationEngine engine;
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("fileio", &fileIO);
engine.load(/* Path to your qml */);
//qml
ListView {
id: listView1
x: 0
y: 0
model: fileio.m_model
delegate: Rectangle{
Text {text: modelData }
}
}
To elaborate on Francisco's answer: if the data of the model is subject to change while the program is running, it is indeed the cleanest solution to implement the QStringList as a Q_PROPERTY, because that will send signals to QML objects when the model data changes. A good example is here.
According to the Qt Manual there is also another option:
Note: There is no way for the view to know that the contents of a QStringList have changed. If the QStringList changes, it will be necessary to reset the model by calling QQmlContext::setContextProperty() again.
An (untested) example of this technique is demonstrated below:
main.cpp:
QStringList updateStringList() {
// Your code from "I create QStringList" goes here.
}
QStringList myStringList = updateStringList();
QQmlApplicationEngine engine;
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("m_model", &myStringList);
engine.load("ListView.qml");
// ... more code ...
myStringList = updateStringList();
ctx->setContextProperty("m_model", &myStringList);
ListView.qml:
ListView {
id: listView1
x: 0
y: 0
model: fileio.m_model
delegate: Rectangle{
Text {text: modelData }
}
}

Qt5.6 QML, why are dynamic models destroyed after garbage collection?

I have a variable number of components, so i'm trying to give each one its own model. In this example, i just create one, but the idea is the same.
GC() is a bit random, so in the example, i force the gc() after a click to flush out the problem. What happens is that the model is destroyed and becomes null. after that the click method cannot use it.
main.qml:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.2
import com.example.qml 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
// builder of dynamic models
ModelFactory { id: maker }
Column
{
anchors.fill: parent
Repeater
{
// create dynamic model
model: maker.makeModel();
delegate: Label
{
id: label
text: model.name
MouseArea
{
anchors.fill: parent
onClicked:
{
// works once until gc()
console.log("clicked on " + model.name)
// wont work anymore. model is destroyed
gc();
}
}
}
}
}
}
c++/mymodel.h:
#include <QAbstractListModel>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QString>
#include <QDebug>
class BoxModel : public QAbstractListModel
{
Q_OBJECT
public:
~BoxModel()
{
// see that it does get destroyed
qDebug() << "~BoxModel()";
}
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return 5;
}
QVariant data(const QModelIndex &index, int role) const override
{
int ix = index.row();
if (ix < 1) return "Larry";
if (ix < 2) return "Barry";
if (ix < 3) return "Gary";
if (ix < 4) return "Harry";
return "Sally";
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole+1] = "name";
return roles;
}
};
class ModelFactory: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
};
main.cpp just registers the types:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<BoxModel>("com.example.qml", 1, 0, "BoxModel");
qmlRegisterType<ModelFactory>("com.example.qml", 1, 0, "ModelFactory");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
what you see:
Click on any of the names. it will work once and after that they will be undefined because model becomes null.
eg
qml: clicked on Sally
~BoxModel()
qml: clicked on undefined
My question is why is this, when i still have a reference to it?
In the example, onClicked could be changed to label.text rather than model.name to fix, but the real problem is that, in general, the model is accessed by the object at any time, for any data. For example, when the box needs to redraw. randomly the data is gone, depending on GC.
I've tried making c++ manage the life of the dynamic model. this could work if i know when exactly QML has finished with it.
thanks for info and ideas.
running on windows 8.1/qt5.6mingw
EDIT1: files as a gist,
https://gist.github.com/anonymous/86118b67ec804e6149423c14792f312d
As Kuba said, this does indeed seem like a bug. However, you can take another approach and take ownership of the models yourself via QQmlEngine::setObjectOwnership(). Specifically, changing
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
to
Q_INVOKABLE BoxModel* makeModel()
{
BoxModel *model = new BoxModel(this);
QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
return model;
}
will fix this (remember to parent the returned model to BoxModel so it gets deleted appropriately). The reason for the behaviour is explained here:
Generally an application doesn't need to set an object's ownership explicitly. QML uses a heuristic to set the default ownership. By default, an object that is created by QML has JavaScriptOwnership. The exception to this are the root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which have CppOwnership by default. The ownership of these root-level objects is considered to have been transferred to the C++ caller.
Objects not-created by QML have CppOwnership by default. The exception to this are objects returned from C++ method calls; their ownership will be set to JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots, but not to property getter invocations.
I just had the same problem with a ComboBox.
As a workaround, you may create your own property to keep a strong reference to it:
Repeater {
property QtObject myModel: maker.makeModel();
model: myModel
// …
}
I know this is an old question, but I've just faced similar issue, and found your question in process of writing mine. See QObject gets destroyed after being put into QML variable for full story, and I'll cite it here.
What I've figured out that if I set the parent of that QObject before I pass it into QML, then it doesn't get deleted. So, I've concluded that passing unparented QObject into QML scope makes that scope become a parent of QObject and call its destructor after scope ends.

Qt5 QML, why does model inside a model cause missing model variable? [duplicate]

I have a variable number of components, so i'm trying to give each one its own model. In this example, i just create one, but the idea is the same.
GC() is a bit random, so in the example, i force the gc() after a click to flush out the problem. What happens is that the model is destroyed and becomes null. after that the click method cannot use it.
main.qml:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.2
import com.example.qml 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
// builder of dynamic models
ModelFactory { id: maker }
Column
{
anchors.fill: parent
Repeater
{
// create dynamic model
model: maker.makeModel();
delegate: Label
{
id: label
text: model.name
MouseArea
{
anchors.fill: parent
onClicked:
{
// works once until gc()
console.log("clicked on " + model.name)
// wont work anymore. model is destroyed
gc();
}
}
}
}
}
}
c++/mymodel.h:
#include <QAbstractListModel>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QString>
#include <QDebug>
class BoxModel : public QAbstractListModel
{
Q_OBJECT
public:
~BoxModel()
{
// see that it does get destroyed
qDebug() << "~BoxModel()";
}
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return 5;
}
QVariant data(const QModelIndex &index, int role) const override
{
int ix = index.row();
if (ix < 1) return "Larry";
if (ix < 2) return "Barry";
if (ix < 3) return "Gary";
if (ix < 4) return "Harry";
return "Sally";
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole+1] = "name";
return roles;
}
};
class ModelFactory: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
};
main.cpp just registers the types:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<BoxModel>("com.example.qml", 1, 0, "BoxModel");
qmlRegisterType<ModelFactory>("com.example.qml", 1, 0, "ModelFactory");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
what you see:
Click on any of the names. it will work once and after that they will be undefined because model becomes null.
eg
qml: clicked on Sally
~BoxModel()
qml: clicked on undefined
My question is why is this, when i still have a reference to it?
In the example, onClicked could be changed to label.text rather than model.name to fix, but the real problem is that, in general, the model is accessed by the object at any time, for any data. For example, when the box needs to redraw. randomly the data is gone, depending on GC.
I've tried making c++ manage the life of the dynamic model. this could work if i know when exactly QML has finished with it.
thanks for info and ideas.
running on windows 8.1/qt5.6mingw
EDIT1: files as a gist,
https://gist.github.com/anonymous/86118b67ec804e6149423c14792f312d
As Kuba said, this does indeed seem like a bug. However, you can take another approach and take ownership of the models yourself via QQmlEngine::setObjectOwnership(). Specifically, changing
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
to
Q_INVOKABLE BoxModel* makeModel()
{
BoxModel *model = new BoxModel(this);
QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
return model;
}
will fix this (remember to parent the returned model to BoxModel so it gets deleted appropriately). The reason for the behaviour is explained here:
Generally an application doesn't need to set an object's ownership explicitly. QML uses a heuristic to set the default ownership. By default, an object that is created by QML has JavaScriptOwnership. The exception to this are the root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which have CppOwnership by default. The ownership of these root-level objects is considered to have been transferred to the C++ caller.
Objects not-created by QML have CppOwnership by default. The exception to this are objects returned from C++ method calls; their ownership will be set to JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots, but not to property getter invocations.
I just had the same problem with a ComboBox.
As a workaround, you may create your own property to keep a strong reference to it:
Repeater {
property QtObject myModel: maker.makeModel();
model: myModel
// …
}
I know this is an old question, but I've just faced similar issue, and found your question in process of writing mine. See QObject gets destroyed after being put into QML variable for full story, and I'll cite it here.
What I've figured out that if I set the parent of that QObject before I pass it into QML, then it doesn't get deleted. So, I've concluded that passing unparented QObject into QML scope makes that scope become a parent of QObject and call its destructor after scope ends.

How to print a list of custom properties of a QML object?

Having created a QML QtObject:
QtObject {
property int p: 42
property int q: 44
}
After storing it in a local variable QObject *obj, how to print all custom property names and possibly values (i.e. only p and q for the example above)? I'd like this to work for any class (not just QtObject) and not print properties that were already declared with Q_PROPERTY.
Clarification: by "custom" I do not mean properties added through QObject::setProperty that were not declared using Q_PROPERTY. I mean properties declared in QML through property <type> <name>: <value> notation that were not declared using Q_PROPERTY in QObject subclass for that QML object. Quick test shows that those properties do not come up in QObject::dynamicPropertyNames.
C++ and QML approach
To print only dynamic property names, you can use the very aptly named dynamicPropertyNames() function of QObject from within an invokable C++ function:
#include <QGuiApplication>
#include <QtQml>
class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(int staticProperty READ staticProperty)
public:
Object() {
setProperty("dynamicProperty", 1);
}
int staticProperty() const {
return 0;
}
Q_INVOKABLE void printDynamicPropertyNames() const {
qDebug() << dynamicPropertyNames();
}
};
int main(int argc, char** argv)
{
QGuiApplication app(argc, argv);
Object object;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("object", &object);
engine.load("main.qml");
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
width: 400
height: 400
visible: true
Component.objectName: object.printDynamicPropertyNames()
}
Output:
("dynamicProperty")
QML-only approach
If you want to print all properties of an object, dynamic or not, purely with QML, use JavaScript's for...in syntax:
The for..in statement iterates over the enumerable properties of an object, in arbitrary order. For each distinct property, statements can be executed.
import QtQuick 2.2
Rectangle {
id: rect
width: 360
height: 360
Component.onCompleted: {
for (var prop in rect) {
print(prop += " (" + typeof(rect[prop]) + ") = " + rect[prop]);
}
}
}
Output:
qml: objectName (string) =
qml: parent (object) = null
qml: data (object) = [object Object]
qml: resources (object) = [object Object]
qml: children (object) = [object Object]
qml: x (number) = 0
qml: y (number) = 0
qml: z (number) = 0
qml: width (number) = 360
qml: height (number) = 360
qml: opacity (number) = 1
qml: enabled (boolean) = true
...
All information about properties, invokable methods (slots too) and signals are stored by QMetaObject in every QObject. If you want to list all properties in an object:
QObject *obj = findObjectMethod();
QMetaObject *meta = obj->metaObject();
int n = meta->propertyCount();
for(int i = 0; i < n; ++i)
{
qDebug() << "Property: " << meta->property(i)->name();
}
To get "your" properties of an object in QML
Component.onCompleted: {
for(var property in rect)
if (property in rect.parent === false) {
console.log("local property:", property, typeof(rect[property]))
}
}

Resources