How to invoke QML methods from C++? - qt

I have my QML file which contains methods. These methods need to be called from C++ by using QMetaObject::invokeMethod().
QML :
// MyItem.qml
import QtQuick 2.0
Item {
function signalBroker(name, value) {
if (name == "volume_radio") {
updateVolume(value);
} else if (name == "mute_radio") {
updateMute();
}
}
}

Not sure why you wanna rely on Javascript but here is the complete code.
sample.pro
QT += qml quick
QT -= gui
SOURCES += main.cpp
RESOURCES += resources.qrc
main.cpp
#include <QCoreApplication>
#include <QQmlEngine>
#include <QQmlComponent>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QQmlEngine engine;
QQmlComponent component(&engine, "qrc:/main.qml");
QObject *object = component.create();
QVariant name = "volume_radio";
QVariant value = 24;
QMetaObject::invokeMethod(object, "signalBroker",
Q_ARG(QVariant, name),
Q_ARG(QVariant, value));
delete object;
return app.exec();
}
resources.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
main.qml
import QtQuick 2.0
Item {
function updateVolume(value) {
console.log('Volume from C++ = ' + value)
}
function updateMute() {
console.log('Radio is now mute.')
}
function signalBroker(name, value) {
if (name === 'volume_radio') {
updateVolume(value)
} else if (name === 'mute_radio') {
updateMute()
}
}
}
When I run it I get the following output
qml: Volume from C++ = 24
Please Note: On main.cpp when loading the main.qml file please make sure to provide a full path (or :/main.qml) or a qrc URL or the compiler will not find the QML file you are trying to load.
Your app can be a GUI app or a console app, it does not matter so long as you include the corresponding header files and update your .pro file.

Related

My custom C++ plugin is not being consumed by the qml test application

I am new to Qt and learning few of the modules in Qt-Qml from youtube and Qt documentation. I want to create cpp plugin which is to be consumed by the qml application.
For this, I have created two projects. One is for creating a plugin library and another application is for consuming the plugin.
I am able to create the plugin library successfully. But, not able to consume it by the test application.
The test application is compiled successfully but throwing below error during run time.
QQmlApplicationEngine failed to load component
qrc:/main.qml:3:1: module "MyPlugin" is not installed
I have read the Qt documentation and googled a lot, but could not able to resolve it.
Qt : 5.15.7
Qt Creator: 5.0.3
OS: Ubuntu 18.04
Project 1: Creating Plugin library
CreatePlugin.pro
TEMPLATE = lib
TARGET = CreatePlugin
QT += qml quick
CONFIG += plugin c++11
DESTDIR = ../pluginFiles
TARGET = $$qtLibraryTarget($$TARGET)
uri = MyPlugin
# Input
SOURCES += \
createplugin_plugin.cpp \
myplugin.cpp
HEADERS += \
createplugin_plugin.h \
myplugin.h
DISTFILES = qmldir
!equals(_PRO_FILE_PWD_, $$OUT_PWD) {
copy_qmldir.target = $$OUT_PWD/qmldir
copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir
copy_qmldir.commands = $(COPY_FILE) "$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)"
"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)"
QMAKE_EXTRA_TARGETS += copy_qmldir
PRE_TARGETDEPS += $$copy_qmldir.target
}
qmldir.files = qmldir
unix {
installPath = $$[QT_INSTALL_QML]/$$replace(uri, \., /)
qmldir.path = $$installPath
target.path = $$installPath
INSTALLS += target qmldir
}
# Copy the qmldir file to the same folder as the plugin binary
cpqmldir.files = qmldir
cpqmldir.path = $$DESTDIR
COPIES += cpqmldir
createplugin_plugin.h
#ifndef CREATEPLUGIN_PLUGIN_H
#define CREATEPLUGIN_PLUGIN_H
#include <QQmlExtensionPlugin>
class CreatePluginPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
void registerTypes(const char *uri) override;
};
#endif // CREATEPLUGIN_PLUGIN_H
createplugin_plugin.cpp
#include "createplugin_plugin.h"
#include "myplugin.h"
#include <qqml.h>
void CreatePluginPlugin::registerTypes(const char *uri)
{
// #uri MyPlugin
qmlRegisterType<MyPlugin>(uri, 1, 0, "MyPlugin");
}
myplugin.h
#ifndef MYPLUGIN_H
#define MYPLUGIN_H
#include <QObject>
#include <QString>
class MyPlugin : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(MyPlugin)
public:
explicit MyPlugin(QObject *parent = nullptr);
~MyPlugin() override;
public slots:
QString getString();
};
#endif // MYPLUGIN_H
myplugin.cpp
#include "myplugin.h"
MyPlugin::MyPlugin(QObject *parent)
: QObject(parent)
{
// By default, QQuickItem does not draw anything. If you subclass
// QQuickItem to create a visual item, you will need to uncomment the
// following line and re-implement updatePaintNode()
// setFlag(ItemHasContents, true);
}
MyPlugin::~MyPlugin()
{
}
QString MyPlugin::getString()
{
return "This is CPP String";
}
qmldir
module MyPlugin
plugin CreatePlugin
Project 2: TestPlugin
TestPlugin.pro
QT += quick
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before
Qt 6.0.0
SOURCES += \
main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import MyPlugin 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
MyPlugin {
id: mypluginId
}
Component.onCompleted: console.log(mypluginId.getString())
}

QT load resource files for debugging

So I've created a qrc file and have a file at /stylesheets/main.qss.
I have stylesheet information in this main.qss file. My goal here is to have a qss file I can work out of and potentially hot reload over time. My issue is that when I debug there is no /stylesheets/main.qss in the debug build location. So it loads the file as an empty string, don't even get an exception. What am I missing?
main.qss
/*#MainBackgroundColor = rgb(40,40,40)*/
/*#MainBorderColor = rgb(0,102,255)*/
/*#MainTextColor = rgb(255,255,255)*/
*
{
color: rgb(255,255,255);
background-color: rgb(40,40,40);
}
QStatusBar
{
border-top: 3px solid rgb(0,102,255);
}
Loading the stylesheet
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
//We want a frameless window.
setWindowFlags(Qt::FramelessWindowHint);
//Load the style sheet into the window
QFile File(":/stylesheets/main.qss");
File.open(QFile::ReadOnly);
QString stylesheet = QLatin1String(File.readAll());
//Setup the UI
ui->setupUi(this);
this->setStyleSheet(stylesheet);
}
MainWindow::~MainWindow()
{
delete ui;
}
resources.qrc
<RCC>
<qresource prefix="/">
<file>stylesheets/main.qss</file>
</qresource>
</RCC>
.pro file
#-------------------------------------------------
#
# Project created by QtCreator 2019-02-20T18:02:31
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = SmartDraw
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp \
stylesheetloader.cpp
HEADERS += \
mainwindow.h \
stylesheetloader.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
resources.qrc
DISTFILES += \
stylesheets/main.qss
EDIT: Found the solution. Apparently Qt isn't very good about updating everything if you happen to have the pro file open. If something super obviously wrong happens like this you need to run Build->Clean All then Build->Run QMake to get everything stituated again.
What I do is keep the stylesheet file (*.qss) next to the app while debugging. Then load it in main.cpp and subscribe for changes using QFileSystemWatcher.
This way I can edit the *.qss file with a nice editor, like SublimeText, and every time I save it, I can see the changes inmediatly:
#include "mydialog.h"
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QSharedPointer>
#include <QFileSystemWatcher>
typedef QSharedPointer<QFileSystemWatcher> QWatcherPtr;
void setStyleSheet(QApplication &a, const QString &strPath, const bool &subscribe = false)
{
// set stylesheet
QFile f(strPath);
if (!f.exists())
{
qDebug() << "[ERROR] Unable to set stylesheet," << strPath << "file not found.";
}
else
{
// set stylesheet
f.open(QFile::ReadOnly | QFile::Text);
QTextStream ts(&f);
a.setStyleSheet(ts.readAll());
f.close();
// subscribe to changes (only once)
if (!subscribe)
{
return;
}
QWatcherPtr watcher = QWatcherPtr(new QFileSystemWatcher);
watcher->addPath(strPath);
QObject::connect(watcher.data(), &QFileSystemWatcher::fileChanged, &a,
[&a, watcher, strPath]()
{
setStyleSheet(a, strPath, false);
});
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// set stylesheet and subscribe to changes
setStyleSheet(a, "./style.qss", true);
MyDialog w;
w.show();
return a.exec();
}

QtQuick, how to know if a application was compiled on debug or release mode?

At Qt/C++ there is QT_DEBUG define macro to know when it is compiled at debug or release.
Is there any method to know if is the application running in debug o release mode inside a QML file?
You can use context properties (or QQmlApplicationEngine::setInitialProperties() since Qt 5.14) to expose C++ objects to QML:
#include <QtGui/QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
#include "qtquick2applicationviewer.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
#ifdef QT_DEBUG
viewer.rootContext()->setContextProperty("debug", true);
#else
viewer.rootContext()->setContextProperty("debug", false);
#endif
viewer.setMainQmlFile(QStringLiteral("qml/quick/main.qml"));
viewer.showExpanded();
return app.exec();
}
main.qml:
import QtQuick 2.2
Item {
id: scene
width: 360
height: 360
Text {
anchors.centerIn: parent
text: debug
}
}
It's not possible to determine this purely from within QML.
You need to know it in runtime or in compile time? Macros are used in compile time, QML is executed in runtime, so there are no difference for compiled application between "debug" and "release".
Solution:
Create a class with const property declared in next way:
class IsDebug : public QObject
{
QOBJECT
Q_PROPERTY( IsDebug READ IsCompiledInDebug ) // Mb some extra arguments for QML access
public:
bool IsCompiledInDebug() const { return m_isDebugBuild; }
IsDebug()
#ifdef QT_DEBUG
: m_isDebugBuild( true )
#else
: m_isDebugBuild( false )
#endif
{}
private:
const bool m_isDebugBuild;
}

How to make QML WebView Element use Qt::openUrlExternally for all 'new windows'?

How to make QML WebView Element use Qt::openUrlExternally instead of some visual newWindowComponent?
I don't think there is a simple way to do this using QML. You can do this with the standard QWebView, but there isn't a way to access this functionality from within QML. You would need to re-wrap QWebView and expose more functions.
An example using the standard C++ interfaces:
test.cpp
#include <QtGui>
#include <QtWebKit>
#include "handler.hpp"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QWebView view;
view.load(QUrl::fromUserInput("http://qt-project.org/"));
view.page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
LinkHandler handler;
QObject::connect(
&view, SIGNAL(linkClicked(const QUrl&)),
&handler, SLOT(open(const QUrl&)));
view.show();
return app.exec();
}
handler.hpp
#ifndef _HANDLER_HPP_
#define _HANDLER_HPP_
#include <QtGui>
class LinkHandler : public QObject
{
Q_OBJECT
public:
LinkHandler();
public slots:
void open(const QUrl& url);
};
#endif
handler.cpp
#include "handler.hpp"
LinkHandler::LinkHandler() : QObject() {}
void LinkHandler::open(const QUrl& url)
{
QDesktopServices::openUrl(url);
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
find_package(Qt4 4.8 REQUIRED QtCore QtGui QtWebkit)
include(${QT_USE_FILE})
qt4_wrap_cpp(MOC_FILES handler.hpp)
add_executable(test
test.cpp
handler.hpp
handler.cpp
${MOC_FILES})
target_link_libraries(test ${QT_LIBRARIES})
The following code will open all links in new windows, but it is trivial to add detection for target=_blank
WebView{
id: webView
url: "samples/sample.html"
preferredWidth: parent.width
enabled: false
onLoadFinished: {
evaluateJavaScript(' \
var els = document.getElementsByTagName("a"); \
for (var i in els){ \
els[i].onclick = function(e){e.preventDefault(); qml.qmlCall(this.getAttribute("href")); return false;} \
} \
')
enabled = true;
}
javaScriptWindowObjects: QtObject {
WebView.windowObjectName: "qml"
function qmlCall(url) {
console.log(url);
Qt.openUrlExternally(url)
}
}
}
It boils down to adding some javascript in the webview after it has loaded to override the default action of links and pass the value of the href attribute to qml, and open externally from there.

File not found during compilation

What is wrong with the code bellow? When I compile it I get a warning that file not found. Something is invalid. I'm probably making a few mistakes here. I think the problem is perhaps with the way I inherit from QWidget.
#include <QtGui/QApplication>
#include "filedialogs.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FileDialogs w;
w.openFile();
return 0;
}
#ifndef FILEDIALOGS_H
#define FILEDIALOGS_H
#include <QWidget>
class QFileDialog;
class FileDialogs : public QWidget
{
public:
FileDialogs(QWidget *parent = 0);
~FileDialogs();
void openFile();
};
#endif // FILEDIALOGS_H
#include <QFileDialog>
#include "filedialogs.h"
FileDialogs::FileDialogs(QWidget *parent)
: QWidget(parent)
{
}
FileDialogs::~FileDialogs()
{
}
void FileDialogs::openFile()
{
QString filename = QFileDialog::getOpenFileName(
this,
tr("Open Document"),
QDir::currentPath(),
tr("Document files (*.doc *.rtf);;All files (*.*)") );
if( !filename.isNull() )
{
qDebug( filename.toAscii() );
}
}
#-------------------------------------------------
#
# Project created by QtCreator 2011-07-29T19:06:33
#
#-------------------------------------------------
QT += core gui
TARGET = exX
TEMPLATE = app
SOURCES += main.cpp\
filedialogs.cpp
HEADERS += filedialogs.h
This error message is emitted by the MOC compiler. You are missing the Q_OBJECT macro. Put it in your class declaration like this:
class FileDialogs : public QWidget
{
Q_OBJECT
public:
....
I know this Question is very old. But in my case it was another problem.
I had to include the path of the headers manually in the .pro file.
INCLUDEPATH += src/subdir

Resources