I was reading the Qt documentation for the past couple of hours trying to figure out a way to make a UI created with Qt Quick UI (QML) communicate (interact) with C++ code (functions... etc.).
I've read the 5 or 6 similar questions on here to but I'm kind of confused, I have problems figuring out where to start or what to do first.
I'd appreciate it a lot if someone could take the time and list the steps needed to make this work.
What i've done so far. I tried doing ...>add new Item> C++ class but I failed with an error saying:" failed to add one or more files to project"> It seems like the files (.. .cpp and .h) are created, they were in the folder where the other project files were but not included in the project.
What I wanna do is just something simple like changing the text of the textedit through a C++ function or any other way possible.
//Test.qml (main.qml)
import QtQuick 2.1
import QtQuick.Window 2.0
Rectangle {
id: rootRect
width: Screen.width/2
height: Screen.height/2
color: "gray"
Button{}
Rectangle{
id: textField
width: 120
height: 40
color: "white"
x:274; y: 61
border.color: "blue"
border.width: 4
radius: 2
}
TextEdit {
id: display
x: 274
y: 61
width: 80
height: 20
text: qsTr("Text Edit")
font.pixelSize: 22
color: "black"
anchors.centerIn: textField
}
Rectangle{
id: inputField
width: textField.width
height: textField.height
border.color: "green"
border.width: 3
color: "white"
x: 140; y: 61
}
TextEdit{
id: input
color: "red"
font.pixelSize: 30
anchors.centerIn: inputField
text: "Some Text"
}
}
//Button.cpl
import QtQuick 2.0
import QtQuick.Window 2.0
Item {
property string defaultText: "New Text"
Rectangle{
id: button
width: rootRect.width/6
height: rootRect.height/8
color: "black"
x: 200; y: 200
radius: 10
}
MouseArea{
id: buttonClickArea
width: 0
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.fill: button
onClicked: {
display.text = defaultText
}
}
}
Thank you for taking the time to read this and/or any replies.
Using Qt 5.4.0 and Qt Creator 3.3.0, create New Project:
Click New Project
Qt Quick Application
Click Choose...
Name the project and select where to place it
Click Next
Select Qt Quick 2.4 from the drop-down menu
Click Next
Select desired Kit(s)
Click Next
Click Finish
Now You should see open main.qml file with following code:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
Qt.quit();
}
}
}
Change it to:
import QtQuick 2.4
import QtQuick.Window 2.2
//### New Code ###
import QtQuick.Controls 1.3
//################
Window {
id: window1
visible: true
//### New Code ###
width: 400
height: 500
TextArea {
id: textArea
readOnly: true
anchors.bottom: textInput.top
anchors.bottomMargin: 6
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: parent.left
anchors.leftMargin: 7
anchors.top: parent.top
anchors.topMargin: 7
}
TextField {
id: textInput
y: 470
height: 23
anchors.right: sendButton.left
anchors.rightMargin: 6
anchors.bottom: parent.bottom
anchors.bottomMargin: 7
anchors.left: parent.left
anchors.leftMargin: 7
}
Button {
id: sendButton
x: 328
y: 470
width: 64
height: 23
text: qsTr("Send")
anchors.bottom: parent.bottom
anchors.bottomMargin: 7
anchors.right: parent.right
anchors.rightMargin: 8
onClicked: {
CppClass.sendMessage(textInput.text, textArea);
textInput.text = "";
}
}
//################
}
Add C++ Class to Your project:
Right Mouse Click project name in Projects viewer
Click Add New...
Select C++ Class if not already selected
Click Choose...
In Class name filed enter "CppClass"
Set Base class to QObject
Click Next
Click Finish
Open cppclass.h and change it to:
#ifndef CPPCLASS_H
#define CPPCLASS_H
#include <QObject>
//### New Code ###
#include <QQuickItem>
#include <QQuickTextDocument>
#include <QTextDocument>
//################
class CppClass : public QObject
{
Q_OBJECT
public:
explicit CppClass(QObject *parent = 0);
~CppClass();
//### New Code ###
Q_INVOKABLE void sendMessage(const QString &msg, QQuickItem *textArea);
//################
signals:
public slots:
};
#endif // CPPCLASS_H
Open cppclass.cpp and change it to:
#include "cppclass.h"
CppClass::CppClass(QObject *parent) : QObject(parent)
{
}
CppClass::~CppClass()
{
}
//### New Code ###
void CppClass::sendMessage(const QString &msg, QQuickItem *textArea)
{
QTextDocument *textDocument = textArea->property("textDocument").value<QQuickTextDocument*>()->textDocument();
textDocument->setHtml(textDocument->toHtml() + "\n<b>Text sent to Cpp side:</b> <i>" + msg + "</i>");
}
//################
Open main.cpp and change it to:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
//### New Code ###
#include <QQmlContext>
#include "cppclass.h"
//################
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
//### New Code ###
CppClass cppClass;
engine.rootContext()->setContextProperty("CppClass", &cppClass);
//################
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Run Your application, type some text to Input Field and click send.
In response to Dynamic Remo comment here's another way to have QML and C++ communicate. This approach is based on C++ emitting a signal and QML acting upon it. Following is the code to get it working.
cppclass.h
#ifndef CPPCLASS_H
#define CPPCLASS_H
#include <QObject>
#include <QDateTime>
class CppClass : public QObject
{
Q_OBJECT
public:
explicit CppClass(QObject *parent = 0);
~CppClass();
Q_INVOKABLE void getCurrentTime();
signals:
void timeUpdate(QString currentTime);
public slots:
};
#endif // CPPCLASS_H
cppclass.cpp
#include "cppclass.h"
CppClass::CppClass(QObject *parent) : QObject(parent)
{
}
CppClass::~CppClass()
{
}
void CppClass::getCurrentTime()
{
emit timeUpdate(QDateTime::currentDateTime().toString("ddd dd MMMM yyyy hh:mm:ss.zzz"));
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "cppclass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
CppClass cppClass;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("CppClass", &cppClass);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
Window {
id: rootWindow
width: 400
height: 400
visible: true
Connections {
target: CppClass
onTimeUpdate: {
initailizeDllMsg.text = currentTime
}
}
Text {
id: initailizeDllMsg
text: qsTr("{current time placeholder}")
font.pointSize: 14
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
Button {
id: button1
x: 163
y: 357
text: qsTr("Show current time")
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
onClicked: CppClass.getCurrentTime()
}
}
Related
I have been trying to fix this issue for 2 days now.I have a gmaps QML project , which I've Integrated into my Qt widgets project using the following code:
gmap locator;
ui->quickWidget->setSource(QUrl::fromLocalFile("/maps/main.qml"));
ui->quickWidget->rootContext()->setContextProperty("gmap",&locator);
ui->quickWidget->show();
When I set the location on the map from the MainWindow using
locator.setData( "28.6082819", "77.0350079");
It works as expected , however , when I add a toolbutton and do the same thing from its slot , it doesn't work.I've connected it to a custom slot in my MainWindow like this:
connect(ui->toolButton_5, SIGNAL(clicked()), this, SLOT(plotmap(QString(ui->lineEdit_3->text()),QString(ui->lineEdit_4->text()),&locator,&ui->quickWidget)));
and here's the custom slot:
void MainWindow::plotmap(QString lat , QString lon, gmap *loc,QQuickWidget *view)
{
loc->setData("02.60","77.04");
view->show();
view->update();
}
Here's my qml files
gmap.cpp
#include "gmap.h"
gmap::gmap()
{
}
void gmap::setData(QString lat,QString lang)
{
qDebug(lat.toLatin1());
emit getLat(lat.toDouble());
emit getLang(lang.toDouble());
}
gmap.h
#ifndef GMAP_H
#define GMAP_H
#include <QObject>
class gmap : public QObject
{
Q_OBJECT
public:
gmap();
signals:
void getLat(double lat);
void getLang(double lang);
public slots:
void setData(QString lat,QString lang);
};
#endif // GMAP_H
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2;
import QtPositioning 5.6;
import QtLocation 5.9
import Qt3D.Input 2.1
import QtQuick.Controls 2.2;
Window {
width: Qt.platform.os == "android" ? Screen.width : 512
height: Qt.platform.os == "android" ? Screen.height : 512
visible: true
Plugin {
id: mapPlugin
name: "osm"
PluginParameter {
name: 'osm.mapping.highdpi_tiles'
value: !!1 }
}
Connections{
target: gmap
onGetLat : mapmarker.center.latitude = lat
}
Connections{
target: gmap
onGetLang : mapmarker.center.longitude = lang
}
Connections{
target: gmap
onGetLang : map.center = QtPositioning.coordinate(mapmarker.center.latitude,mapmarker.center.longitude,150);
}
Map {
id: map
anchors.fill: parent
anchors.rightMargin: -15
anchors.bottomMargin: -10
anchors.leftMargin: 15
anchors.topMargin: 10
plugin: mapPlugin
center: QtPositioning.coordinate() // NSUT
zoomLevel: 14
activeMapType: supportedMapTypes[2]
Button {
x: 389
y: 445
text: "ADD MARKER"
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 27
anchors.rightMargin: 23
padding: 7
onClicked: gmap.setData(textField.text,textField1.text)
}
MapCircle {
id: mapmarker
center {
latitude: 28.6078
longitude: 77.0406
}
radius: 50.0
color: 'green'
border.width: 3
}
TextField {
id: textField
x: 176
y: 397
text: qsTr("")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 75
anchors.rightMargin: 136
}
TextField {
id: textField1
x: 176
y: 445
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 27
anchors.rightMargin: 136
font.hintingPreference: Font.PreferDefaultHinting
}
}
}
The map in my QQuickWidget doesn't update.What could I be doing wrong , please let me know.
AFAIK your connect syntax is wrong.
Store locator as a member somewhere, in MainWindow for example:
public:
void setLocator(gmap* loc) { m_locator = loc; }
private:
gmap* m_locator;
Add a slot to MainWindow:
private slot:
plotmapFromLineEdits() { plotmap(ui->lineEdit_3->text(), ui->lineEdit_4->text(), m_locator, ui->quickWidget); }
and connect it like this:
connect(ui->toolButton_5, SIGNAL(clicked()), this, SLOT(plotmapFromLineEdits()));
Or in C++11 or above you can connect without defining a slot by using lambda:
connect(ui->toolButton_5, SIGNAL(clicked()), [&](){plotmap(ui->lineEdit_3->text(), ui->lineEdit_4->text(), m_locator, ui->quickWidget);}));
I don't think you have to show and update QQuickWidget every time. Just delete the quickwidget parameter.
Lastly please make sure your class names start with an uppercase letter:
gmap => GMap
I don't know a lot about qt so please bear with me.
I have a qt widgets application that I made as a part of a project , a friend has a Qt quick applications project which I'd like to integrate into my qt widgets app.This is what I've added in my mainwindow.cpp
QQuickView *view = new QQuickView();
QWidget *container = QWidget::createWindowContainer(view, this);
container->setMinimumSize(300, 200);
container->setMaximumSize(600, 400);
view->setSource(QUrl("/maps/main.qml")); file.
ui->qmlwidget->addWidget(container);
However , the integration doesn't work correctly.The qt quick project is a map which takes coordinates as inputs and then points them on the map.It works as expected when it is run standalone but when I integrate it using the above method , clicking the button which is supposed to point out the location does nothing.This is the Qt quick app code:
gmap.cpp
#include "gmap.h"
gmap::gmap()
{
}
void gmap::setData(QString lat,QString lang)
{
qDebug(lat.toLatin1());
emit getLat(lat.toDouble());
emit getLang(lang.toDouble());
}
gmap.h
#ifndef GMAP_H
#define GMAP_H
#include <QObject>
class gmap : public QObject
{
Q_OBJECT
public:
gmap();
signals:
void getLat(double lat);
void getLang(double lang);
public slots:
void setData(QString lat,QString lang);
};
#endif // GMAP_H
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2;
import QtPositioning 5.6;
import QtLocation 5.9
import Qt3D.Input 2.1
import QtQuick.Controls 2.2;
Window {
width: Qt.platform.os == "android" ? Screen.width : 512
height: Qt.platform.os == "android" ? Screen.height : 512
visible: true
Plugin {
id: mapPlugin
name: "osm"
PluginParameter {
name: 'osm.mapping.highdpi_tiles'
value: !!1 }
}
Connections{
target: gmap
onGetLat : mapmarker.center.latitude = lat
}
Connections{
target: gmap
onGetLang : mapmarker.center.longitude = lang
}
Connections{
target: gmap
onGetLang : map.center = QtPositioning.coordinate(mapmarker.center.latitude,mapmarker.center.longitude,150);
}
Map {
id: map
anchors.fill: parent
anchors.rightMargin: -15
anchors.bottomMargin: -10
anchors.leftMargin: 15
anchors.topMargin: 10
plugin: mapPlugin
center: QtPositioning.coordinate() // NSUT
zoomLevel: 14
activeMapType: supportedMapTypes[2]
Button {
x: 389
y: 445
text: "ADD MARKER"
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 27
anchors.rightMargin: 23
padding: 7
onClicked: gmap.setData(textField.text,textField1.text)
}
MapCircle {
id: mapmarker
center {
latitude: 28.6078
longitude: 77.0406
}
radius: 50.0
color: 'green'
border.width: 3
}
TextField {
id: textField
x: 176
y: 397
text: qsTr("")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 75
anchors.rightMargin: 136
}
TextField {
id: textField1
x: 176
y: 445
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 27
anchors.rightMargin: 136
font.hintingPreference: Font.PreferDefaultHinting
}
}
}
in your qml side,there is no item that was named to gmap. you must first add a gmap object to rootContext
gmap newGmap;
view->rootContext()->setContextProperty("gmap", &newGmap);
I use open source qt5.9 for an embedded device.
I wanna to use virtual keyboard in my qml project. I know I should add a static link in .pro file like :
static {
QT += svg
QTPLUGIN += qtvirtualkeyboardplugin
}
and also add
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
to main.cpp file to use the virtual keyboard. but my virtual keyboard does not fire when I click on my text object:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.VirtualKeyboard 2.1
Window {
visible: true
width: 640
height: 480
TextInput {
id: textInput;
text:"ssssss"
height: 120;
width: parent.width - 2;
anchors.bottom: keyboard.top
color: "#000000"; // black
// http://doc.qt.io/qt-5/qinputmethod.html#properties
focus: Qt.inputMethod.visible;
verticalAlignment: TextInput.AlignVCenter;
}
}
It solved.
just put the kind of input for each lineedit . like this :
InputPanel{
id:inputpanel
visible:active
y:active?parent.height - inputpanel.height : parent.height
anchors.left: parent.left
anchors.right: parent.right
}
TextInput{
id:input
inputMethodHints: Qt.ImhDigitsOnly
focus: Qt.inputMethod.visible;
text: "123211"
}
TextInput{
id:input2
anchors.top:input.bottom
inputMethodHints: Qt.ImhLowercaseOnly
focus: Qt.inputMethod.visible;
text: "123211"
}
I am trying to build a simple media player in QML . I can't use QFile Dialog as its a single window application running on EGLFS . I managed so far to build a simple file browser for QML and can play a mp3 from a fixed location. But here is where I am stuck:
How do I set the current selected file from the treeview as source for my audio ?
How can I get the Audio play each file with file ending mp3 from the selected folder?
Thanks for your help
.pro file
QT += qml quick multimedia widgets
CONFIG += c++11
SOURCES += main.cpp
RESOURCES += qml.qrc
QML_IMPORT_PATH =
QML_DESIGNER_IMPORT_PATH =
DEFINES += QT_DEPRECATED_WARNING
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtQml>
#include <QFileSystemModel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setOrganizationName("Power-Tune");
app.setOrganizationDomain("power-tune.org");
app.setApplicationName("PowerTune");
QQmlApplicationEngine engine;
//
QFileSystemModel model;
model.setRootPath("/");
engine.rootContext()->setContextProperty("my_model", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
my main.qml :
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import QtMultimedia 5.8
ApplicationWindow {
visible: true
width: 800
height: 480
minimumWidth: 800
minimumHeight: 480
title: qsTr("PowerTune ")
// visibility: "FullScreen"
color: "black"
Rectangle {
width: parent.width
height: parent.height
property bool playing: false
Audio {
id: playMusic
//source: currently selected file in TreeView
}
Button {
id: previous
text: "previous"
width: 100
anchors.right: playpause.left
// onClicked: select previous item in current folder
}
Button {
id: playpause
text: "play/pause" //playing ? "Stop music" : "Start music"
width: 100
anchors.right: next.left
onClicked: {
if(playing == true) {
playMusic.stop()
playing = false
} else {
playMusic.play()
playing = true
}
}
}
Button {
id: next
text: "next"
width: 100
anchors.right: parent.right
}
TreeView {
id:mp3selector
width: parent.width/2
height: parent.height
TableViewColumn {
title: "Name"
role: "fileName"
width: 300
}
model: my_model
}
}
}
Couple of notes:
property bool playing: false
Properties are better to be defined at top of hierarchy so that they accessible in all children, so prefer to put it directly in ApplicationWindow not in Rectangle element.
And, I think TreeView is not a suitable choice (Its a Quick Controls 1.0 element not available in Quick Controls 2.0 , and for mixing check this post);
You can do the model directly from QML, using a ListView with FolderListModel, you only need an additional of decorations for highlighting and selecting of files with mouse .. that's all, TreeView could be replaced with below code, it works and fancy!
...
import QtQuick.Controls 2.2
import Qt.labs.folderlistmodel 2.1
...
ListView {
id: list
width: parent.width/2
height: parent.height
model: folderModel
onCurrentIndexChanged: {
// This will handle changing playlist with all possible selection methods
playMusic.source = folderModel.get(currentIndex, "fileURL")
}
FolderListModel {
id: folderModel
folder: "file:///MP3/"
showDirs: false
nameFilters: ["*.mp3"]
}
delegate: Component {
Item {
width: parent.width
height: 40
Column {
Text { text: fileName }
}
MouseArea {
anchors.fill: parent
onClicked: {
list.currentIndex = index
}
}
}
}
highlight: Rectangle {
color: 'grey'
}
focus: true
}
For your buttons onClicked just handle currentIndex of the ListView , for example in the next button add:
onClicked: list.currentIndex += 1
you also need to add some code to avoid getting index out of range or -1 ...etc.
I get the "qrc:/main_left.qml:23: ReferenceError: CppClass is not defined" when I run the below code. This code tries to change the position of a rectangle in a window.
Main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "cppclass.h"
#include "bcontroller.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//QGuiApplication app(argc, argv);
BController c;
CppClass cppClass;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("CppClass", &cppClass);
engine.load(QUrl(QStringLiteral("qrc:/main_left.qml")));
return app.exec();
}
main_left.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
Rectangle {
visible: true
width: 640
height: 480
property int index: 0
Text {
text: controller.name
anchors.centerIn: parent
}
Image{
id:imageLeft
anchors.fill: parent
source:"imageLeft.jpg";
}
Connections {
target: CppClass
onPosUpdate: {
rect.x = currentPos
}
}
Button {
id: button1
x: 163
y: 357
text: qsTr("Change Position")
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
onClicked: CppClass.getCurrentPos()
}
Rectangle {
id: rect
width: parent.width/2
height: parent.height/2
color: "transparent"
border.color: "red"
border.width: 5
radius: 10
}
MouseArea {
anchors.fill: parent
onClicked: controller.setName(++index)
}
}
cppclass.cpp
#include "cppclass.h"
#include <QtQuick>
#include <string>
CppClass::CppClass(QObject *parent) : QObject(parent)
{
}
CppClass::~CppClass()
{
}
void CppClass::getCurrentPos()
{
int pos = rand() % 400;
std::string s = std::to_string(pos);
QString qstr = QString::fromStdString(s);
emit posUpdate(qstr);
}
Please help!
I think there is a problem with CppClass declaration in your main.cpp => CppClass cppClass; and your CppClass constructor is CppClass::CppClass(QObejct *parent); which means that you are missing the constructor parameter.
Therefore,you have two possibilities
1st : Try use your class without QObject *parent
2nd: provide the QObject* parent for the contructor of CppClass when declaring it in main.cpp