I try
ApplicationWindow {
onActiveFocusControlChanged: {
console.log(activeFocusControl)
console.log(activeFocusControl.objectName)
}
}
ouput:
qml: QQuickTextField(0xa6ec00) //the 'activeFocusControl'
qml: //the 'activeFocusControl.objectName'
qml: QQuickButton(0xd7ccb0)
qml:
I want to
onActiveFocusControlChanged: {
if (activeFocusControl.className == "QQuickTextField") {
//do something
}
else if (activeFocusControl.className == "QQuickButton") {
//do something
}
but the "className" method does not exist
so how i can do it?
sorry, my english is pool, and thank you
There is no method to access the className from qml, so a possible solution is to create a helper from c ++ as shown below:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QObject>
class Helper : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QString getClassName(QObject *obj) const{
return obj? obj->metaObject()->className(): "";
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
Helper helper;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("helper", &helper);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
then it is used on the QML side:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button {
id: button
x: 270
y: 47
text: qsTr("Button")
}
TextField {
id: textField
x: 220
y: 169
text: qsTr("Text Field")
}
onActiveFocusControlChanged: {
var className = helper.getClassName(activeFocusControl)
switch(className){
case "QQuickTextField":
console.log("I am QQuickTextField")
break
case "QQuickButton":
console.log("I am QQuickButton")
break
default:
console.log("empty")
}
}
}
The complete example can be found at the following link.
Related
According to http://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html chapter "Invoking QML Methods", I try to invoke the bbox function within main.qml from C++.
Here is my code :
main.qml
import QtQuick 2.0
import QtQuick.Window 2.0
import QtLocation 5.6
import QtPositioning 5.6
Window {
width: 512
height: 512
visible: true
Item{
anchors.fill: parent
Plugin{
id: osmplugin
name: "osm"
}
Map {
anchors.fill: parent
id: map
plugin: osmplugin;
zoomLevel: (maximumZoomLevel - minimumZoomLevel)/2
center {
// The Qt Company in Oslo
latitude: 59.9485
longitude: 10.7686
}
}
Component.onCompleted:{
console.log("zoomlevel : " + map.zoomLevel)
console.log("Visible region : " + map.visibleRegion)
}
function bbox(){
return map.visibleRegion;
}
}
}
and main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QGeoRectangle>
#include <QQmlComponent>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlComponent component(&engine, "qrc:/main.qml");
QObject *map = component.create();
QVariant ret;
bool ok = QMetaObject::invokeMethod( map, "bbox", Qt::DirectConnection, Q_RETURN_ARG( QVariant, ret ) );
if ( !ok ){
qWarning( "Fail to call qml method" );
}
QGeoRectangle rect = qvariant_cast<QGeoRectangle>( ret );
return app.exec();
}
I get the following error message :
"QMetaObject::invokeMethod: No such method QQuickWindow::bbox()"
Seems it does not find the bbox function. Could you help me to find the problem ?
Thanks in advance.
Give your item an object name:
Item{
anchors.fill: parent
objectName: "bboxObj"
function bbox(){
return map.visibleRegion;
}
}
In C++ get your Object using the object name:
QObject *map = component.create();
QObject *bboxObj= map->findChild<QObject*>("bboxObj");
and invoke the method:
if (bboxObj)
ok = QMetaObject::invokeMethod( bboxObj, "bbox", Qt::DirectConnection, Q_RETURN_ARG( QVariant, ret ) );
I tried to operate a part of a qt project in Qt\Examples\Qt-5.9\quick\views, I am new to qml and I am trying to open each time a different QDialog window depending on qml pathview component that has been clicked. First of all, I started with creating a class (interfacageQML) which will serve to interface the qml Mainform and the QDialog (qtinterface), the necessary files are included among which interfacageqml.h.
here is the main.cpp :
#include "interfacageqml.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<interfacageQML>("Interfacage", 1, 0,"Component:MouseArea");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
And here's interfacageqml.h :
#ifndef INTERFACAGEQML_H
#define INTERFACAGEQML_H
#include <QObject>
#include "qtinterface.h"
class interfacageQML : public QObject
{
Q_OBJECT
public:
interfacageQML(QObject *parent);
~interfacageQML();
Q_INVOKABLE void mouseClick();
signals:
void clicked();
};
#endif // INTERFACAGEQML_H
interfacageqml.cpp :
#include "interfacageqml.h"
#include <QDebug>
#include <QApplication>
interfacageQML::interfacageQML(QObject *parent)
: QObject(parent)
{
}
interfacageQML::~interfacageQML()
{
}
void interfacageQML::mouseClick()
{
qDebug() << "qmlinterface::mouseClick()";
emit clicked();
}
My project is organised this way :
the qmlinterface.qrc file contains these paths:
main.qml :
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MainForm{
anchors.fill: parent
}
}
MainForm.qml :
import QtQuick 2.6
import QtQuick.Controls 2.0 as QQC2
import Interfacage 1.0
Rectangle {
width: 800
height: 800
color: "white"
ListModel {
id: appModel
ListElement {
name: "Contacts"
icon: "pics/Resources/AddressBook_48.png"
}
ListElement {
name: "Music"
icon: "pics/Resources/AudioPlayer_48.png"
}
ListElement {
name: "Movies"
icon: "pics/Resources/VideoPlayer_48.png"
}
ListElement {
name: "Camera"
icon: "pics/Resources/Camera_48.png"
}
ListElement {
name: "Calendar"
icon: "pics/Resources/DateBook_48.png"
}
ListElement {
name: "Todo List"
icon: "pics/Resources/TodoList_48.png"
}
}
Component {
id: appDelegate
Item {
width: 100
height: 100
scale: PathView.iconScale
Image {
id: myIcon
y: 20
anchors.horizontalCenter: parent.horizontalCenter
source: icon
}
Text {
anchors {
top: myIcon.bottom
horizontalCenter: parent.horizontalCenter
}
text: name
}
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
Interfacage.mouseClick
}
}
}
}
Component {
id: appHighlight
Rectangle {
width: 100
height: 80
color: "lightsteelblue"
}
}
PathView {
id: view
anchors.fill: parent
highlight: appHighlight
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
path: Path {
startX: 50
startY: 80
PathAttribute {
name: "iconScale"
value: 2.0
}
PathQuad {
x: 250
y: 200
controlX: 50
controlY: 200
}
PathAttribute {
name: "iconScale"
value: 2.0
}
PathQuad {
x: 600
y: 50
controlX: 400
controlY: 200
}
PathAttribute {
name: "iconScale"
value: 2.0
}
}
}
}
When I run this project, i got an error :
error:C2280
However, when I comment this line : qmlRegisterType<interfacageQML>("Interfacage", 1, 0, "Component:MouseArea"); the project runs and I can navigate between the pathview components in the MainForm.
When you use qmlRegisterType you are registering a new data type in QML, it is not an object, in that case the name "Component: MouseArea" is not suitable.
qmlRegisterType<interfacageQML>("Interfacage", 1, 0, "InterfacageQML");
Another error is that you must pass a parent by default, in this case 0 or nullptr since the items may not have parents.
class interfacageQML : public QObject
{
Q_OBJECT
public:
explicit interfacageQML(QObject *parent = nullptr);
[...]
As I said in the first lines, this is a new type, it is not an object so you must create it.
import QtQuick 2.6
import QtQuick.Controls 2.0 as QQC2
import Interfacage 1.0
Rectangle {
width: 800
height: 800
color: "white"
InterfacageQML{
id: myitem
}
[...]
And in the end if you want to use it you must call the function through the item.
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
myitem.mouseClick()
}
}
Since you want to connect your QDialog with the QML through that class, you can not do it since they will be different objects, one solution for this is to use a singleton, for this you must do the following:
interfacageqml.h
#ifndef INTERFACAGEQML_H
#define INTERFACAGEQML_H
#include <QObject>
#include <QQmlEngine>
class interfacageQML : public QObject
{
Q_OBJECT
static interfacageQML* instance;
explicit interfacageQML(QObject *parent = nullptr);
public:
static interfacageQML *getInstance();
~interfacageQML();
Q_INVOKABLE void mouseClick();
signals:
void clicked();
};
#endif // INTERFACAGEQML_H
interfacageqml.cpp
#include "interfacageqml.h"
#include <QDebug>
interfacageQML* interfacageQML::instance = 0;
interfacageQML *interfacageQML::getInstance()
{
if (instance == 0)
instance = new interfacageQML;
return instance;
}
interfacageQML::interfacageQML(QObject *parent) : QObject(parent)
{
}
interfacageQML::~interfacageQML()
{
}
void interfacageQML::mouseClick()
{
qDebug() << "qmlinterface::mouseClick()";
emit clicked();
}
main.cpp
#include "interfacageqml.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
static QObject *singletonTypeProvider(QQmlEngine *, QJSEngine *)
{
return interfacageQML::getInstance();
}
int main(int argc, char *argv[])
{
qmlRegisterSingletonType<interfacageQML>("Interfacage", 1, 0, "InterfacageQML", singletonTypeProvider);
// test
interfacageQML *obj = qobject_cast<interfacageQML*>(interfacageQML::getInstance());
QObject::connect(obj, &interfacageQML::clicked,[]{
qDebug()<<"clicked";
});
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
As it is a singleton it is not necessary to create an item, you can do it directly:
import Interfacage 1.0
[...]
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
InterfacageQML.mouseClick()
}
}
This last example can be found in the following link.
I have an enum defined in a QObject with a few values, and I am registering the enum as QFlags as the Qt documentation specifies. I have registered the enum and the QObject as metatypes that I can access just fine from QML.
The problem is that once I have a C++ QObject slot defined that has the QFlags as an argument it doesn't get an error when it is called, but instead passes in the first defined value in the enum (ie. its value is that of the enum entry with the number 0).
It is hard to describe, so I created a small working example (using C++11/Qt 5.7). When you run it and click anywhere in the window that opens, QFlags<QMLThing::MyEnum>(VALA) is printed out, even though in main.qml I am calling thing.doThing(QMLThing.VALC).
I started by creating a "Qt Quick Application" in QtCreator. Then added a class called "QMLThing". Here is the source code for each file:
QMLThing.hpp
#ifndef QMLTHING_HPP
#define QMLTHING_HPP
#include <QObject>
class QMLThing : public QObject
{
Q_OBJECT
public:
enum MyEnum {
VALA = 0,
VALB = 1,
VALC = 2,
VALD = 4,
};
Q_ENUM(MyEnum)
Q_DECLARE_FLAGS(MyEnums, MyEnum)
public:
explicit QMLThing(QObject *parent = 0);
public slots:
void doThing(QMLThing::MyEnums val);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMLThing::MyEnums)
Q_DECLARE_METATYPE(QMLThing::MyEnums)
#endif // QMLTHING_HPP
QMLThing.cpp
#include "QMLThing.hpp"
#include <QDebug>
QMLThing::QMLThing(QObject *parent) : QObject(parent)
{}
void QMLThing::doThing(QMLThing::MyEnums val)
{
qDebug() << val;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "QMLThing.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<QMLThing>("stuff", 1, 0, "QMLThing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import stuff 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
thing.doThing(QMLThing.VALC)
}
}
Text {
text: qsTr("Click here and look in the terminal")
anchors.centerIn: parent
}
QMLThing {
id: thing
}
}
This seems a lot like a bug, but maybe I'm just missing something.
You're missing Q_FLAG(MyEnums):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QObject>
class QMLThing : public QObject
{
Q_OBJECT
public:
enum MyEnum {
VALA = 0,
VALB = 1,
VALC = 2,
VALD = 4,
VALE = VALC | VALD
};
Q_DECLARE_FLAGS(MyEnums, MyEnum)
Q_FLAG(MyEnums)
public:
explicit QMLThing(QObject *parent = 0) :
QObject(parent)
{
}
public slots:
void doThing(QMLThing::MyEnums val)
{
qDebug() << val;
}
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMLThing::MyEnums)
Q_DECLARE_METATYPE(QMLThing::MyEnums)
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<QMLThing>("stuff", 1, 0, "QMLThing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.7
import QtQuick.Window 2.2
import stuff 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
thing.doThing(QMLThing.VALC)
thing.doThing(QMLThing.VALC | QMLThing.VALD)
}
}
Text {
text: qsTr("Click here and look in the terminal")
anchors.centerIn: parent
}
QMLThing {
id: thing
}
}
As mentioned here, you don't need to use Q_ENUM():
Note: The Q_FLAG macro takes care of registering individual flag
values with the meta-object system, so it is unnecessary to use
Q_ENUM() in addition to this macro.
Not sure exactly what is going on but first of all:
public:
enum MyEnum {
VALA,
VALB,
VALC,
VALD,
};
You need to remove the last coma.
I would also recommend to set at least the first enum, to a certain value, usually 0 so you know where you are going but no need to set the following enum items as they will be auto-incremented from the last one set.
Last, I'm not entirely sure about the QMLThing.ValC, shouldn't it be QMLThing::MyEnums::ValC ?
Using qt 5.5, qt quick controls 1.4 and the below qt creator boilerplate code: what is the most FORMAL way to invoke C++ code in response to a button (just debug text to screen)?
// main cpp
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
and the QML file inside the qml.qrc:
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button {
id: add
x: 248
y: 222
text: qsTr("add")
}
}
I am aware of this as a possible answer but it looks a very complicated way to just hook a button to a code! If this is The Formal way to use Qt 5.5 and QML then this should be the answer.
As you can see in the documentation, you have many options:
The class can be registered as an instantiable QML type. This was the option proposed by #BaCaRoZzo
The class can be registered as a Singleton Type
An instance of the class can be embedded into QML code as a context property or context object
The Qt QML module also provides ways to do the reverse and manipulate QML objects from C++ code. This was the option proposed by #hyde
In your case, I'd prefer the last option because it requires fewer lines of code.
Example:
main.cpp
// main cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myclass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *item = engine.rootObjects().first();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
signal qmlSignal(string msg)
Button {
id: add
x: 248
y: 222
text: qsTr("add")
onClicked: qmlSignal(text)
}
}
myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
#endif // MYCLASS_H
I made an example to show both approaches mentioned by #BaCaRoZzo :
// main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "myclass.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
MyClass myclass;
engine.rootContext()->setContextProperty("_myclass", &myclass);
QObject *item = engine.rootObjects().first();
QObject::connect(item, SIGNAL(qmlSignal(QString)), &myclass, SLOT(cppSlot(QString)));
return app.exec();
}
The header file of the c++ class that is invoked from qml:
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
signals:
public slots:
void count();
void cppSlot(const QString &msg);
};
#endif // MYCLASS_H
and its implementation:
#ifndef MY_CLASS_H
#define MY_CLASS_H
// myclass.cpp
#include "myclass.h"
#include <QDebug>
MyClass::MyClass(QObject *parent) : QObject(parent)
{
}
void MyClass::count()
{
static int i = 0;
i++;
qDebug() << "wow =" + QString::number(i) ;
}
void MyClass::cppSlot(const QString &msg)
{
qDebug() << "Called the C++ slot with message:" << msg;
}
#endif
The user interface qml file with two buttons that show both approaches:
//main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
signal qmlSignal(string msg)
Button {
id: button
x: 218
y: 229
width: 148
height: 31
text: qsTr("run cpp method ctxt prop")
onClicked: _myclass.count()
}
Button {
id: button1
x: 218
y: 300
width: 148
height: 23
text: qsTr("run cpp method qmlsignal")
onClicked: qmlSignal(text)
}
}
I implemented a subclass of QQuickPaintedItem to be used in QML which works on its own when registered through
qmlRegisterType<T>
Instances of this class are created at application startup and put into a QList that is stored inside a subclass of QAbstractItemModel. I thought I could easily return each of those objects in the model's data method and use them as the QML ListViews delegate.
It now looks like this:
Model.cpp:
QVariant AbteilungsModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && role == Qt::DisplayRole)
{
Abteilung* a = static_cast<Abteilung*>(index.internalPointer());
return QVariant::fromValue(a);
}
}
main.qml:
ListView {
id: abteilungenListView
anchors.fill: parent
spacing: 5
model: abteilungen
delegate: modelData
}
I, of course, made the model available in QML via
void QQmlContext::setContextProperty(const QString & name, QObject * value)
but I don't know how to properly declare the ListViews delegate, since "modelData" doesn't work.
Does anyone have an idea if this is even possible or do you guys have a better solution?
Any help is appreciated! :)
It might be possible, but it goes against the whole MVC idea. Your model shouldn't know about your delegates. As a simplified example:
main.cpp
#include <QGuiApplication>
#include <QtQml>
#include <QtQuick>
class Abteilung : public QQuickPaintedItem
{
Q_OBJECT
public:
Abteilung() {
}
void paint(QPainter *painter) {
painter->setPen(Qt::red);
painter->drawRect(boundingRect().adjusted(0, 0, -painter->pen().width(), -painter->pen().width()));
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<Abteilung>("Test", 1, 0, "Abteilung");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
import Test 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
ListView {
id: abteilungenListView
anchors.fill: parent
spacing: 5
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < 100; ++i) {
append({name: i});
}
}
}
delegate: Abteilung {
width: abteilungenListView.width
height: 40
}
}
}