Here's what I've in my QML:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640
height: 480
title: "Test Window"
ComboBox{
width: 300
model: testContext.List
delegate: ItemDelegate{
width: parent.width
contentItem: RowLayout{
Text{ text: modelData.name }
Text{
text: " | " + modelData.age
Layout.alignment: Text.AlignRight
}
}
background: Rectangle{ color: hovered? "green" : "white" }
}
}
}
When I click on the ComboBox I see items in the popup list BUT the selected item doesn't appear in the box!
If I set textRole: "name", it shows only the name property in the box but I want the whole formatted text, defined in ItemDelegate, in the box.
Here in the illustration they've one more contentItem beside the one in delegate:
contentItem: Text {
...
text: control.displayText
...
}
It still doesn't show the formatted text in the box even if I add the additional contentItem in my QML.
EDIT
Here's the ViewModel .h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QVector>
#include "aclass.h"
#include "Property.h"
class Test : public QObject
{
Q_OBJECT
PROPERTY(QVector<AClass*>, List)
public:
explicit Test(QObject *parent = nullptr);
private:
QQmlApplicationEngine engine;
};
#endif // TEST_H
the .cpp:
#include "test.h"
Test::Test(QObject *parent) : QObject(parent)
{
engine.rootContext()->setContextProperty("testContext", this);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
for(int i = 0; i < 10; i++){
auto a = new AClass();
a->setname("Item " + QString::number(i));
a->setage(i + 10);
m_List.push_back(a);
}
emit ListChanged();
}
and the main.cpp:
#include <QGuiApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Test test;
return app.exec();
}
for the Q_PROPERTY I've a macro PROPERTY with the following content:
#ifndef PROPERTY_H
#define PROPERTY_H
#define PROPERTY(QType, name) \
Q_PROPERTY(QType name READ name WRITE set##name NOTIFY name##Changed) \
public: \
QType name(){return m_##name;} \
void set##name(QType value){m_##name = value; emit name##Changed();} \
Q_SIGNAL void name##Changed(); \
private: \
QType m_##name;
#endif // PROPERTY_H
Here's AClass:
#ifndef ACLASS_H
#define ACLASS_H
#include <QObject>
#include "Property.h"
class AClass : public QObject
{
Q_OBJECT
PROPERTY(QString, name)
PROPERTY(int, age)
};
#endif // ACLASS_H
For that I've to use the additional contentItem outside the ItemDelegate like this:
contentItem: RowLayout{
Text{ text: model[control.currentIndex].name }
Text{
text: " | " + model[control.currentIndex].age
horizontalAlignment: Text.AlignRight
Layout.fillWidth: true
}
}
and I've to give the ComboBox an id, here in the example control is the id
Related
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.
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 need ContextMenu in my qml widgets.
I have a solution: create QGraphicsProxyWidget, which contain QMenu, but there is a problem: the context menu is not visible outside the main window. How to set main window as parent of menu?
Custom components is a bad idea - I need possibilities of the QMenu: exec, actions, pop-up and other.
Main.qml
import QtQuick 1.1
import CustomComponents 1.0
Rectangle {
width: 360
height: 360
QMLContextMenu {
id: menu
}
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.LeftButton
onClicked: {
if(mouse.button === Qt.RightButton)
menu.exec(mouse.x, mouse.y);
else
Qt.quit();
}
}
}
main.cpp
#include <QApplication>
#include "qmlapplicationviewer.h"
#include <QtCore>
#include <QtDeclarative>
#include <QtGui>
class QMLContextMenu : public QGraphicsProxyWidget
{
Q_OBJECT
public:
QMLContextMenu(QGraphicsItem* parent = 0) : QGraphicsProxyWidget(parent)
{
menuWidget = new QMenu("my menu");
setWidget(menuWidget);
}
public slots:
QString exec(int x, int y)
{
menuWidget->clear();
menuWidget->addAction("hello world!");
menuWidget->addSeparator();
menuWidget->addAction("or not...");
//menuWidget->show();
QAction *pResultAction = menuWidget->exec(QPoint(x, y));
QString text;
if(pResultAction)
text = pResultAction->text();
return text;
}
private:
QMenu *menuWidget;
};
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
qmlRegisterType<QMLContextMenu>("CustomComponents", 1, 0, "QMLContextMenu");
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/quick1/main.qml"));
viewer.showExpanded();
return app->exec();
}
Create you own rectangle with listview. But in this way there are lots of problems because in qml 1 qml widgets can't be top-level windows. I did that:
Create separate ContextMenu.qml
Use loader to instantiate it
show it when I need it