Connecting qml-signals to Qt - c++

I'm trying to use a qml-grid view in my code. I'm trying to couple it with my C++ code.
I've dynamically created a list view model and passed across the qml file. It works fine.
However, I'm facing trouble when I want to connect a Qml signal to Qt/c++ code. I've handled mouseArea in my Qml-rectangle and emitting a signal from there.
I'm trying to connect to the signal as follows:
QDeclarativeView *pQMLContainer = NULL;
TempWidget *pTemp = new TempWidget();
pQMLContainer = new QDeclarativeView(pTemp);
pQMLContainer->setResizeMode(QDeclarativeView::SizeRootObjectToView);
pQMLContainer->rootContext()->setContextProperty("imgModel", createModel() );
pQMLContainer->setSource(QUrl("../Temp/qml/gridview-example.qml"));
QObject *rootObject = dynamic_cast<QObject*>pQMLContainer->rootObject();
QObject::connect(rootObject, SIGNAL(keyPressed()), pTemp, SLOT(onKeyPressed()));
When the connect statement runs, I get an error: cannot connect to "null" object.
On debugging, I found I could never get "rootObject" as a valid pointer.
Where am I going wrong?
Thanks

Can you try this ? (it is example code from Qt Docs)
QObject *item = pQMLContainer->rootObject();
QObject::connect(item, SIGNAL(keyPressed()),
pTemp, SLOT(onKeyPressed()));

The code is pretty much straight:
in .cpp file:
ui->declarativeView->setSource(QUrl("qrc:/Resources/main.qml"));
QGraphicsObject *obj = ui->declarativeView->rootObject();
connect ( obj, SIGNAL(clicked()), this, SLOT(itemClicked()));
and QML File:
import Qt 4.7
Rectangle {
width: 100
height: 100
id: rect
signal clicked
Text {
text: "Hello World"
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
rect.clicked();
}
}
}
one more thing, check the location of your qml file, it should be accessible to the binary.

Perhaps you should use qobject_cast instead of dynamic_cast? See e.g. question
dynamic_cast returns NULL but it shouldn't

QGraphicsObject is a QObject so no cast should be required. If your compiler complains, try adding #include <QGraphicsObject>.
Just casting without the compiler knowing the classes is asking for trouble. (Especially as there is multiple inheritance involved.)

I could finally get this working. I'm not sure if this is the real solution to the problem, but finally this got it working:
I was setting the qml path as a relative path to my working folder. And yes the path was indeed correct, as I could see the qml and its contents. I just happened to change the qml path from relative to the working folder to relative to "qrc" as:
pQMLContainer->setSource(QUrl("qrc:/gridview-example.qml"));
instead of:
pQMLContainer->setSource(QUrl("../Temp/qml/gridview-example.qml"));
and it started working. I'm not sure if I had to add the qml to the qrc (I've just started using qml).
Thanks everyone for your support!
Mots

Related

QQuickWidget send signal from c++ to slot in QML

I have a application and I want make a little animation for it.
I did a qml file and used QQuickWidget to open and show it in my display. Now a I want make iteration between c++ and QML. I want, for example, when a function in c++ is called, a ball move in my display. But I could not make a connection between c++ and qml.
Every help is welcome.
A little part of my code:
c++
QQuickWidget *quickWidget = new QQuickWidget;
quickWidget->setSource(QUrl("qrc:/QML/main.qml"));
auto rootObject = quickWidget->rootObject();
// Connect C++ signal to QML slot
connect(this, SIGNAL(cppSignal()), rootObject, SLOT(qmlSlot()));
emit cppSignal();
QML
Rectangle {
id: tela
visible: true
width: 715
height: 77
color: '#E8E8E8'
// NumberAnimation {
// running: true
// target: bolinha
// property: "x"
// duration: 1000
// to: 600
// }
function qmlSlot() {
bolinha.visible= enabled
animBolinha.start();
}
}
enter image description here
What I can do to solve it?
I am not sure if you can call a QML method from C++ code as you did.
The recommended way from QT documentation is:
All QML methods are exposed to the meta-object system. As the functions are exposed to meta-object system, you can use QMetaObject::invokeMethod(), to invoke the QML function.
Probably in your case, you should call as said below (not tested).
auto rootObject = quickWidget->rootObject();
QMetaObject::invokeMethod(rootObject, "qmlSlot");
Look documentation (search for Invoking QML Methods)
As said in documentation, you can use Q_ARG to pass the arguments and Q_RETURN_ARG for receiving return arguments.

ListView not initially showing data

My QML ListView doesn't show my data until I perturb it with the mouse (e.g. just drag it up and down.) After this the view shows the model without issue until it empties, and then I once again need to perturb it to get it working again. Is there way to kick this ListView into working?
I'm using Qt 5.8 on Linux 14.04. My model is a subclass of QAbstractListModel. I build it by following the AbstractItemModel Example. The main difference is that my list model is a property of an entity, rather than being set with setContextProperty in main.cpp.
There are a few similar issues here on SO about the ListViews not updating, but none seem to only have an issue at the start. Most of them relate to the OP calling dataChanged manually instead of beforeInsertRows() & endInsertRows() - both methods I'm calling (see below.)
My ListView is in an item loaded with a SceneLoader.
I posted all the relevant code here, because I'm a little suspicious of how I use the Layouts on my ListView (maybe that's causing it? Maybe my hierarchy is broken? I haven't been able to prove that though.)
In short though,
ListView:
ListView {
anchors.fill: parent
model: sceneGraph.blobs
delegate: delegate
}
BlobModel.cpp:
auto BlobModel::addBlob(const BlobPointDataPtr& data) -> void
{
// ...
// Each blob has a uuid
const auto idx = Contains(uuid);
if (-1 != idx)
{
blobs_[idx]->Update(data);
Q_EMIT dataChanged(createIndex(idx, 0), createIndex(idx, 0));
}
else
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
blobs_ << new Blob{data, id_count_}; id_count_++;
endInsertRows(); // responsible for the QQmlChangeSet
Q_EMIT dataChanged(createIndex(rowCount(), 0), createIndex(rowCount(), 0));
}
}
Also, on my terminal, I receive the message:
QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)
This seems to be emitted by endInsertRows(), but I'm not sure why. In the past the solution has been to register the missing type, e.g. qRegisterMetaType<QQmlChangeSet*>("QQmlChangeSet"); but this seems not to be a public type with Qt, and because everything mostly works without it, I'm not sure missing that is the exact issue.
The problem, as pointed out in the comments, was that I was modifying my model outside of the main thread.
My code was set up so that another thread would trigger additions to my model by directly calling addData. The reason my minimal example wasn't able to replicate this was because in it I used a QTimer to simulate the other thread, however QTimer also runs on the main thread.
The solution was to change my direct call to addData(data) to emitted a signal to do the addition, thus moving the actual work back to the main thread.

Access QML WebView from C++

I have qml file with a webview in it.
I get a reference to that object in c++ but is QObject*
Can i cast it to something similar to QWebView from which i can get the QWebPage object and so?
qobject_cast<> returns NULL if i try QWebView.
It works with cast to QQuickItem but that doesn't really help me much.
Thanks
You can't. The QML WebView element doesn't have a public C++ API. You have two ways to work around this:
Use private API. First, add this to the .pro file:
QT += quick quick-private webkit-private
Then include the private headers:
#include <private/qquickwebview_p.h>
Now you can access the WebView, as long as you get a pointer to it (for example by using QObject::findChildren or by passing the QObject* from QML to C++. Here I simply assume the WebView is the root object, for simplicity):
QQuickWebView * webView = dynamic_cast<QQuickWebView*>(viewer.rootObject());
qDebug() << webView->url();
Use the generic QObject API to access the WebView's properties:
QObject * webView = viewer.rootObject();
qDebug() << webView->property("url");

Weird behavior when switching view between C++ and QML

I'm currently working on a project using Qt 5.0.2 on an embedded linux (ARM Cortex A9).
The main UI interface is developped in QML but I need to be able to hide this view to show a QWebView directly in C++.
I coded a simple view controller in c++ who hide()/show() the QML view and the many instances of QWebView.
The hiding/showing method work fine but when i show back the QML view, it's very instable. QML object are visible (or not visible :p) when they should not and the focus are buggy too. Object are draw in the wrong position too.
I try several methods :
-Initialize the focus/visible property of the differents objects everytime I show the QML view.
-use .setSource() everytime before showing the view
-try to update() the differents object thank to rootObject() before showing the view.
Did anyone have a tips to make the QML view functionnal again after a switch to a c++ view ?
thank.
there is probably a better way but,
you could probably do something like this (I have not tested this):
note: if the slot implementation is wrong (bad math) it will result in infinite recursion.
//this code could probably be in the constructor
real widthOverHeightRatio = 2;//set this value to what you want, or what it is when user first presses shift depending on the use case.
QObject::connect(this, SIGNAL(widthChange()), this, SLOT(onWidthChanged()));
QObject::connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
//don't forget to define these slots in the header
//implemented slots
void MyClass::onWidthChanged()
{
if(width/height!=widthOverHeightRatio){
height = width/widthOverHeightRatio;
}
}
void MyClass::onHeightChanged()
{
if(width/height!=widthOverHeightRatio){
width = height*widthOverHeightRatio;
}
}

Clearing WebView Cache from withing QML

I open website with QDeclarativeView and use JavaScript to load next pages in same view.
After each website loaded, my program occupy 20mb more of memory. How do i clean the cache or otherwise release the memory after new website is loaded?
I tried:
decView->engine()->rootContext()->setContextProperty("myEngine", decView->engine());
and then in qml
myEngine.clearComponentCache()
but i get
TypeError: Result of expression 'myEngine.clearComponentCache' [undefined] is not a function.
What i should do?
EDIT: here is what i got sofar:
aws.cpp
void Aws::openQMLWindowSlot(){
QDeclarativeView *decView= new QDeclarativeView();
decView->engine()->rootContext()->setContextProperty("myAws",this);
decView->setSource(QUrl("qrc:/inc/firstqml.qml"));
decView->show();
}
void Aws::clearCacheQMLSlot(){
//HERE I GOT PROBLEM
}
firstqml.qml
import QtQuick 1.1
import QtWebKit 1.0
WebView {
id: webView
objectName: "myWebView"
url:"http://example.com"
onLoadFinished: {myAws.clearCacheQMLSlot();}
}
There two reasons why your code doesn't work as intended. First, to be able to access slots and invokable methods of QObject descendants, you have to register them:
qmlRegisterType<QDeclarativeEngine>("MyApp", 1, 0, "QDeclarativeEngine");
And second, QDeclarativeEngine::clearComponentCache is neither a slot nor an invokable method, so it would still not work. It is simply impossible to call normal C++ methods from QML.
What you actually have to do is to implement an own QObject based class wrapping the call to QDeclarativeEngine::clearComponentCache in a slot, registering the class like above, set an instance of that class as an context property like you did with the declarative engine and finally call the slot from QML.