If Qml can do
MyComponent.connect(someJsFunction);
how can I do this on c++ ???
I need connect JSValue if it isCallable without workarounds. I want to know how it makes qml engine...
QObject::connect(QObject, signal, QJSValue, evaluateFunctionSlot);
This will work. I got the solution from this SO post. That said, I don't know if it aligns with the Qt way of doing it. Their example of invoking a QML method uses QMetaObject::invokeMethod().
main.cpp
#include <QGuiApplication>
#include <QQuickItem>
#include <QQuickView>
class MyClass : public QObject
{
Q_OBJECT
signals:
void cppSignal(const QString &msg);
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view(QUrl(u"qrc:/75069400/main.qml"_qs));
view.show();
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(&myClass, SIGNAL(cppSignal(QString)),
item, SLOT(callFromCpp(QString)));
emit myClass.cppSignal("this is a test");
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick
Rectangle {
width: 320
height: 240
function callFromCpp(value : string) {
console.log("QML" , value)
}
}
As result the best workaround:
qml
function connect(name, fn){
myObject[name].connect(fn[name]);
}
c++
QMetaObject::invokeMethod(MyObject, "connect", Q_ARG(QVariant, "anySlotName"), Q_ARG(QVariant, QVariant::fromValue(data)));
Related
I hope someone can help me with this. I have an external QML Module which accepts a QStringList as parameter. However, what I have is a simple String. My question is: Is there a way in QML to convert a list of Strings into a QStringList without any external c++ functions?
Thanks
I tried to pass a simple string but it is not accepted.
You can use a JavaScript array of strings or list<string> depending on your Qt version. Have a look here.
main.qml
import QtQuick
Rectangle {
id: root
width: 640
height: 480
property var jsArray: ["apple", "banana", "mango"]
property list<string> stringList: ["Oslo", "Berlin", "New York"]
Component.onCompleted: {
var arr = ["more", "strings", "here"]
applicationData.setSomething(arr)
applicationData.setSomething(root.stringList)
applicationData.setSomething(root.jsArray)
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
class ApplicationData : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void setSomething(const QStringList &list) const
{
for (const auto &s : list)
qDebug() << s;
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
ApplicationData data;
view.rootContext()->setContextProperty("applicationData", &data);
view.setSource(QUrl(u"qrc:/75085103/main.qml"_qs));
view.show();
return app.exec();
}
#include "main.moc"
The way I understand the qtreeview.cpp the expand method in the QTreeView is responsible for expanding nodes. For example it is used in the expandOrCollapseItemAtPos method. I try to redefine a node expansion in the QTreeWidget:
#include <QApplication>
#include <QWidget>
#include <QTreeWidget>
#include <QMessageBox>
class MyTree : public QTreeWidget
{
public:
MyTree(QWidget *parent) : QTreeWidget(parent) {}
expandItem(const QTreeWidgetItem *item) {
QMessageBox msg;
msg.setText("EXPAND ITEM!!");
msg.exec();
QTreeWidget::expandItem(item);
}
expand(const QModelIndex &index) {
QMessageBox msg;
msg.setText("EXPAND!!");
msg.exec();
QTreeWidget::expand(index);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *mainWidget = new QWidget();
mainWidget->resize(200,100);
MyTree *myTree = new MyTree(mainWidget);
myTree->resize(200,100);
QTreeWidgetItem *node, *leaf;
node = new QTreeWidgetItem(myTree);
node->setText(0,"node");
leaf = new QTreeWidgetItem(node);
leaf->setText(0,"leaf");
mainWidget->show();
return a.exec();
}
But there is no any message box when I expand a node. I tried to comment QTreeWidget::expandItem(item); and QTreeWidget::expand(index); but expanding is still working.
How do I redefine a node expanding in a QTreeWidget?
QTreeWidget::expand and QTreeWidget::expandItem are non-virtual methods. So redefinition is not useful. I will use slot-signal mechanism with QTreeWidget::expanded/collapsed signals.
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(myExpand(QModelIndex)));
This should solve your problem -
connect(QTreeWidgetObject, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));
It is very straightforward to connect to QMenu::triggered or QMenu::hovered signals by calling QObject::connect and pass the appropriate QAction.
However, I do not know how to use QMenu::aboutToHide signal, as there is no action passed to that signal.
How to use QMenu::aboutToHide and QMenu::aboutToShow signals or those are just virtual functions that can be overridden?
The signals in the world of Qt are not functions, never invoke them. The signals notify that something has happened with the QObject and send information if necessary.
In the case of triggered and hovered it is necessary to send the QAction because several QActions in a QMenu, then the developer thought that it is necessary to know with which QAction was interacting. On the other hand with aboutToShow and aboutToHide the signal does not send anything because it wants to notify is that if the QMenu was shown or hidden, respectively. Is there any need to know that QMenu was shown or hidden if he did it ? no, because the sender did it, I do not use other properties that we do not have at hand.
Example of use:
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;
QMenu *foo_menu = w.menuBar()->addMenu("Foo Menu");
for(const QString & name: {"action1", "action2", "action3"}){
foo_menu->addAction(name);
}
QObject::connect(foo_menu, &QMenu::aboutToShow, [](){
qDebug()<<"aboutToShow";
});
QObject::connect(foo_menu, &QMenu::aboutToHide, [](){
qDebug()<<"aboutToHide";
});
QObject::connect(foo_menu, &QMenu::triggered, [](QAction *action){
qDebug()<< "triggered: " <<action->text();
});
QObject::connect(foo_menu, &QMenu::hovered, [](QAction *action){
qDebug()<< "hovered: " <<action->text();
});
w.show();
return a.exec();
}
And what happens if you have several QMenu that connect to the same slot? How do I know QMenu was shown or hidden?
The solution is to use sender() which is a method that belongs to the QObject class that returns the object that emitted the signal, in this case the QMenu.
Example:
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QDebug>
class MainWindow: public QMainWindow{
public:
MainWindow(QWidget *parent=nullptr):
QMainWindow(parent)
{
for(const QString & name_of_menubar: {"bar1", "bar2", "bar3"}){
QMenu *menu = menuBar()->addMenu(name_of_menubar);
connect(menu, &QMenu::aboutToShow, this, &MainWindow::on_aboutToShow);
connect(menu, &QMenu::aboutToHide, this, &MainWindow::on_aboutToHide);
for(const QString & name: {"action1", "action2", "action3"}){
menu->addAction(name);
}
}
}
private slots:
void on_aboutToShow(){
if(QMenu *menu = qobject_cast<QMenu *>(sender()))
qDebug()<<"aboutToShow" << menu->title();
}
void on_aboutToHide(){
if(QMenu *menu = qobject_cast<QMenu *>(sender()))
qDebug()<<"aboutToHide" << menu->title();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
This question already has answers here:
Qt5. Embed QWidget object in QML
(5 answers)
Closed 5 years ago.
For a while I thought that I was not able to use Qt c++ classes with qml applications, but I found this: http://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html.
Now I'm trying to create an instantiable object type. I first ran into "Qwidget: Cannot create a Qwidget without QApplication" reading online it the answer seem to be just to change QGuiApplication to QApplication, but then I get: "ASSERT: "
!d->isWidget"
This is the Qt class that I'm trying to use as a qml type: http://doc.qt.io/qt-5/qlcdnumber.html.
Here is my main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QLCDNumber>
#include <QQuickStyle>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<QLCDNumber>("LCDNumber",1,0,"LCDNumber");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
Here is what I'm trying to create in main.qml:
...
import LCDNumber 1.0
Window {
...
LCDNumber{
digitCount: 3
intValue: 1
mode: LCDNumber.Dec
segmentStyle: LCDNumber.Flat
smallDecimalPoint: false
value: 0
}
}
Is it really possible to create a qt c++ class in qml? I'm missing something?
Yes, it's possible!
In your class use the Tags Q_PROPERTY and Q_INVOKABLE to provide into QML access to properties and methods class, like this:
class NameYourClass : public QDeclarativeItem {
Q_OBJECT
Q_PROPERTY(int intProperty1 READ getIntProperty1 WRITE setIntProperty1)
Q_PROPERTY(QString strProperty2 READ getStrProperty2 WRITE setStrProperty2)
private:
int intProperty1;
QString strProperty2;
public:
explicit NameYourClass(QDeclarativeItem *parent = 0);
~NameYourClass();
Q_INVOKABLE int getIntProperty1() const;
Q_INVOKABLE void setIntProperty1(int value);
Q_INVOKABLE QString getStrProperty2() const;
Q_INVOKABLE void setStrProperty2(const QString &value);
}
Your main.cpp:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
qmlRegisterType<NameYourClass>("IdentifierName", 1, 0, "NameYourClass");
return app.exec();
}
Your QML file:
import IdentifierName 1.0
Rectangle {
id: nameRectangle
width: 999
height: 999
onSomethingChange: {
execFunction();
}
property NameYourClass nameDesired: nameObject
NameYourClass {
id: nameObject
intProperty1: 999
}
function execFunction() {
var varExample;
varExample = nameDesired.getIntProperty1();
nameDesired.setIntProperty1(varExample);
}
}
I do not think I've forgotten anything.
I hope it helps!
I dont know how to pass parameters from QML file to c++ file in Qt.
QML code:
import QtQuick 1.1
Rectangle{
id:loin
height: 272
width:480
property alias loguid:loginuid
signal sigHome()
Rectangle{
id:rect1
width:parent.width-80
height:24
TextInput {
id:loginuid
maximumLength: 16
width: maximumLength * 20
focus: false
validator: RegExpValidator { regExp: /\d+/ }
KeyNavigation.down: login1
}
}
Button{
id: login1
x: 195
y: 187
height:30;
focus:false
border.color:"black"
opacity: activeFocus ? 1.0 : 0.5
Text{
text:"LOGIN"
anchors.horizontalCenter:login1.horizontalCenter;
anchors.verticalCenter:login1.verticalCenter;
}
Keys.onReturnPressed: {
if(loginuid.text < 1000000000000000)
{
text1.opacity=0.1
error1.visible=true
errorText.text="\n enter valid 16 digit number\n"
errorOk.focus=true
loginuid.focus=false
}
else{
loginuid.focus=false
loin.sigHome()
}
}
}
}
c++ code:
#include <QApplication>
#include <QDeclarativeView>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
int uid;
QDeclarativeView view;
view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();
return app.exec();
}
When I press the login button rect1.text content shud go to main.cpp file and uid in the main.cpp get dat value. Something like this uid=rect1.text.
How to do it?
I wouldn't try to listen for a QML signal from the C++ side. Calling a C++ method with arguments is much easier and achieves the same:
To do so you have to:
define a slot or invokable method accepting the required arguments
register the class carrying the method with the declarative engine
then you can set an instance of this class as a property of your root context and finally call this method from QML
This topic is also well covered in the official documentation.
Thanks, sebasgo, your response helped me. I used signals and slots to communicate.
I created a signal in main.qml.
signal info(string msg)
and in login page
else{
info(loginUid.text)
loginuid.focus=false
loin.sigHome()
}
and in main.cpp I connected it to d slot
main.cpp goes like this
#include <QtGui>
#include <QApplication>
#include <QDeclarativeView>
#include <QtDeclarative>
class DeclarativeView : public QDeclarativeView
{
Q_OBJECT
public:
DeclarativeView(const QUrl & source) : QDeclarativeView(source)
{
}
public slots:
void readText(QString quid)
{
qdebug<<quid;
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QString file = "main.qml";
QApplication app(argc, argv);
DeclarativeView view(QUrl::fromLocalFile(file));
QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(view.rootObject());
QObject::connect(item, SIGNAL(info(QString)), &view, SLOT(readText(QString)));
view.show();
return app.exec();
}
Create a GUI controller C++ class:
class UiController : public QObject
{
Q_OBJECT
public:
UiController();
virtual ~UiController();
public slots:
void cal_daysoff__onDoubleClicked(const QDate& date);
};
In QML file you define, say, a calendar control in which you connect a signal to a slot in the controller:
Calendar{
id: cal_daysoff
Layout.fillWidth: true
Layout.fillHeight: true
onDoubleClicked: UiController.cal_daysoff__onDoubleClicked(date)
}
In main file, when launching the QML interface, connect the interface to the controller:
#include "uicontroller.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
UiController control;
engine.rootContext()->setContextProperty("UiController", &control);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}