Qt5: Derive QQuickWidget to get a widget - c++

I try to embed this qml definition:
import QtQuick 2.0
Rectangle {
id: mqldefinition
color: "green"
width: 100
height: 100
Text {
id: text
text: "This is a text!"
font.pointSize: 14
anchors.centerIn: parent
}
}
within a Qt C++-widget:
qmlwidget.h
#include <QQuickWidget>
class QmlWidget
: public QQuickWidget
{
Q_OBJECT
public:
explicit QmlWidget(QWidget *parent = nullptr);
};
qmlwidget.h
#include "qmlwidget.h"
QmlWidget::QmlWidget(QWidget *parent)
: QQuickWidget(parent)
{
resize(100,100);
setSource(QUrl("qrc:/definition.qml"));
}
This widget shall be shown within the MainWindow as partly shown here:
mainwindow.cpp
#include "mainwindow.h"
#include "qmlwidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto qmlWidget = new QmlWidget(this);
qmlWidget->move(0,0);
}
But it is not shown :(
I found this in SO:
QQuickWidget *view = new QQuickWidget;
view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
view->show();
but I would like to implement it as derived class :)

One possible solution is to set the QmlWidget as centralWidget.
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QmlWidget *qmlWidget = new QmlWidget;
qmlWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
setCentralWidget(qmlWidget);
}
Another approach is to use layouts to handle the size of the widget.

Related

Connections qml dial and slider in qml

I have a dial and a slider, in mainwindow.cpp I have:
ui->quickWidget_3->setSource(QUrl("qrc:///slider.qml"));
ui->quickWidget_4->setSource(QUrl("qrc:///dial.qml"));
I want that when I move slider dial moves..
I wrote:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtQml>
#include <QtQuick>
#include <QSlider>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setStyleSheet("background-color:white");
ui->quickWidget->engine()->rootContext()->setContextProperty("yourObject",ui->horizontalSlider);
ui->quickWidget_2->engine()->rootContext()->setContextProperty("yourObject1",ui->horizontalSlider_3);
QObject* item = ui->quickWidget_3->rootObject();
QObject::connect(item, SIGNAL(moved()), SLOT(sliderMoved()));
ui->quickWidget_2->setSource(QUrl("qrc:///sl.qml"));
ui->quickWidget->setSource(QUrl("qrc:///qml.qml"));
ui->quickWidget_3->setSource(QUrl("qrc:///slider.qml"));
ui->quickWidget_4->setSource(QUrl("qrc:///dial.qml"));
ui->horizontalSlider->setStyleSheet("QSlider::groove:vertical {background-color:red; position:absolute; left:4px; right: 4px}");
//connect circularGauge e text
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_horizontalSlider_2_sliderMoved(int position)
{
ui->dial->setValue(position);
}
void MainWindow::sliderMoved()
{
QObject* slider = ui->quickWidget_3->rootObject();
QObject* dial = ui->quickWidget_4->rootObject();
qreal value = QQmlProperty::read(slider, "value").toReal();
QQmlProperty::write(dial, "value", value);
}
but it doesn't work
It is well documented here: Interacting with QML Objects from C++
Please try to provide full relevant working code in your questions next time, like I'm going to do in this answer. Doing so, you will increase the chance of an answer.
test.pro
QT += core gui widgets quickwidgets quickcontrols2
[...]
dial.qml
import QtQuick.Controls 2.12
Dial {
id: control
from: 0
to: 100
stepSize: 1
value: 0
}
slider.qml
import QtQuick.Controls 2.12
Slider {
id: control
from: 0
to: 100
stepSize: 1
value: 0
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void sliderMoved();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include <QQmlProperty>
#include <QQuickItem>
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->quickWidget_1->setSource(QUrl("qrc:/slider.qml"));
ui->quickWidget_2->setSource(QUrl("qrc:/dial.qml"));
QObject* item = ui->quickWidget_1->rootObject();
QObject::connect(item, SIGNAL(moved()), SLOT(sliderMoved()));
}
void MainWindow::sliderMoved()
{
QObject* slider = ui->quickWidget_1->rootObject();
QObject* dial = ui->quickWidget_2->rootObject();
qreal value = QQmlProperty::read(slider, "value").toReal();
QQmlProperty::write(dial, "value", value);
}
MainWindow::~MainWindow()
{
delete ui;
}

Changing QML Object value from C++

I created a QML class like
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
Rectangle {
width: 80
height: 200
property double myVal: 15
Timer {
running: true
repeat: true
interval: 1
onTriggered: gauge.value = myVal
}
Gauge {
objectName: "gauge"
id: gauge
anchors.fill: parent
anchors.margins: 10
value: myVal
Behavior on value {
NumberAnimation {
duration: 1000
}
}
style: GaugeStyle {
valueBar: Rectangle {
implicitWidth: 16
color: Qt.rgba(gauge.value / gauge.maximumValue, 0, 1 - gauge.value / gauge.maximumValue, 1)
}
}
}
}
and I want to change value from my c++ file. To do this, I created a method which is setDataToGauge() and this method is like
void MainWindow::setDataToGauge(double newVal){
QQmlApplicationEngine engine;
QQmlComponent component(&engine, QUrl::fromLocalFile("gauge.qml"));
QObject object = component.create();
QObject *myGauge = object->findChild<QObject*>("gauge");
if(myGauge){
myGauge->setProperty("value",newVal);
qDebug() << myGauge->property("value");
}
}
this. However, it doesn't change the value of gauge. I tried different methods but it couldn't find the solution. When I drop
Behavior on value {
NumberAnimation {
duration: 1000
}
}
this part from QML file value is changing but view of gauge is not changing.
Also, I am adding to full code of my c++ file
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QDateTime>
#include <QtQuickWidgets/QQuickWidget>
#include <QtQml>
#include <QObject>
QObject *object;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
m_serial(new QSerialPort(this))
{
ui->setupUi(this);
ui->gaugeWidget->setSource(QUrl::fromLocalFile("gauge.qml"));
ui->gaugeWidget->setUpdatesEnabled(true);
QQmlApplicationEngine engine;
QQmlComponent component(&engine, QUrl::fromLocalFile("gauge.qml"));
object = component.create();
setDataToGauge(60.0);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setDataToGauge(double newVal){
QObject *myGauge = object->findChild<QObject*>("gauge");
if(myGauge){
QQmlProperty::write(myGauge, "value", newVal);
qDebug() << myGauge->property("value");
}
}
What is the problem in there?
In your attempt of solution you are creating another gauge, and that gauge is not shown because the QQmlApplicationEngine is a local variable that will be deleted when it finishes executing, in addition QQmlApplicationEngine expects a Window or ApplicationWindow, not an Item like Rectangle.
On the other hand it is advisable to use a qresource to store the .qml because otherwise you will have to copy them every time you compile the side of your executable.
It is also not recommended to access QML objects from C ++, it is better to export a C ++ object to QML using setContextProperty().
A simple solution is to create a signal, the signal is connected through Connections::
*.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void setDataToGauge(double newVal);
signals:
void dataGaugeChanged(double dataToGauge); // signal
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
*.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QQmlEngine>
#include <QQmlContext>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->gaugeWidget->engine()->rootContext()->setContextProperty("MainWindow", this);
ui->gaugeWidget->setSource(QUrl("qrc:/gauge.qml"));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setDataToGauge(double newVal){
emit dataGaugeChanged(newVal);
}
gauge.qml
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
Rectangle {
width: 80
height: 200
Gauge {
id: gauge
anchors.fill: parent
anchors.margins: 10
Behavior on value {
NumberAnimation {
duration: 1000
}
}
style: GaugeStyle {
valueBar: Rectangle {
implicitWidth: 16
color: Qt.rgba(gauge.value / gauge.maximumValue, 0, 1 - gauge.value / gauge.maximumValue, 1)
}
}
}
Connections{
target: MainWindow
onDataGaugeChanged: gauge.value = dataToGauge
}
}
Another option is to create a Q_PROPERTY and make a binding:
*.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
Q_PROPERTY(double dataGauge READ dataGauge WRITE setDataGauge NOTIFY dataGaugeChanged)
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
double dataGauge() const;
void setDataGauge(double dataGauge);
signals:
void dataGaugeChanged();
private:
Ui::MainWindow *ui;
double mDataGauge;
};
#endif // MAINWINDOW_H
*.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QQmlEngine>
#include <QQmlContext>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->gaugeWidget->engine()->rootContext()->setContextProperty("MainWindow", this);
ui->gaugeWidget->setSource(QUrl("qrc:/gauge.qml"));
}
MainWindow::~MainWindow()
{
delete ui;
}
double MainWindow::dataGauge() const
{
return mDataGauge;
}
void MainWindow::setDataGauge(double dataGauge)
{
if(mDataGauge == dataGauge)
return;
mDataGauge = dataGauge;
emit dataGaugeChanged();
}
*.qml
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
Rectangle {
width: 80
height: 200
Gauge {
id: gauge
anchors.fill: parent
anchors.margins: 10
value: MainWindow.dataGauge // binding
Behavior on value {
NumberAnimation {
duration: 1000
}
}
style: GaugeStyle {
valueBar: Rectangle {
implicitWidth: 16
color: Qt.rgba(gauge.value / gauge.maximumValue, 0, 1 - gauge.value / gauge.maximumValue, 1)
}
}
}
}
Both solutions can be found in the following link.

QML: Type Error with custom QObject

I'm trying out something in QML to try and make it alittle easier to merge the two more seamlessly; to be precise, I'm trying to link an object with structured data to QML.
I have the following setup:
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "dataobject.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<DataObject>("DO", 1,0,"DataObject");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
dataobject.h:
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
#include <QVariant>
#include <iostream>
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal a MEMBER a NOTIFY aChanged)
Q_PROPERTY(qreal b MEMBER b NOTIFY bChanged)
public:
explicit DataObject(QObject *parent = 0);
signals:
void aChanged();
void bChanged();
public slots:
void printState() {
using namespace std;
cout << a << ", " << b << endl;
}
private:
qreal a;
qreal b;
};
#endif // DATAOBJECT_H
dataobject.cpp:
#include "dataobject.h"
DataObject::DataObject(QObject *parent) :
QObject(parent)
{
connect(this, SIGNAL(aChanged()), this, SLOT(printState()));
connect(this, SIGNAL(bChanged()), this, SLOT(printState()));
}
main.qml:
import DO 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
Text {
text: qsTr("Hello World")
MouseArea {
property DataObject myData;
anchors.fill: parent
drag.target: parent
onReleased: {
myData.a = mouse.x;
myData.b = mouse.y;
}
}
}
}
qml.qrc:
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
Now, what I'd hoped was that values generated by QML could be feed into a object in C++ directly (i.e. the onReleased handler in the MouseArea trying to write to the myData field). However, this basic proof of concept doesn't work, but I don't really understand why.
The error I get (on drag and release of the mouse button) is:
qrc:///main.qml:29:TypeError:Type error
Which matches up with the line "myData.a = mouse.x;", so it fails straight away.
Any idea's where I'm going wrong? I've tried with the fields being int, double, QVariant, and qreal, none of which have worked. Is in a fundamental inability in QML to link objects like that? If so, any idea how, for example, anchors.fill is implemented in the Qt source code?
It helps if you break down the expression on the line where the error is coming from. Try just printing myData.a first:
print(myData.a)
myData.a = mouse.x;
myData.b = mouse.y;
qrc:///main.qml:31: TypeError: Cannot read property 'a' of null
So, myData is null. We can verify this with another QObject-based type:
MouseArea {
property DataObject myData;
property Item item
anchors.fill: parent
drag.target: parent
onReleased: {
print(item)
myData.a = mouse.x;
myData.b = mouse.y;
}
}
qml: null
So, you can fix this error by initialising the property:
property DataObject myData: DataObject {}
You can think of QObject-based properties as pointers for JavaScript; they can be null or point to a valid object... or be undefined. :) I can't find anything mentioned about this here, but that's where this behaviour should be mentioned.
If you'd like to simplify things, you can have the object default-constructed for you by making it a child object of the MouseArea, rather than a property:
MouseArea {
DataObject {
id: myData
}
anchors.fill: parent
drag.target: parent
onReleased: {
myData.a = mouse.x;
myData.b = mouse.y;
}
}
Currently, you won't be able to refer to this property from C++. However, you can achieve this in two ways:
Declare a property alias to the item.
Give it an objectName and use QObject::findChild to find it.

How to connect QQuickitem with Qml Object?

I am trying to make a simple program that allow user to connect to the specific websites via clicking the image.
Here is my code:
account.h:
#ifndef ACCOUNTS_H
#define ACCOUNTS_H
#include <QObject>
#include <QUrl>
#include <QDesktopServices>
class accounts : public QObject
{
Q_OBJECT
public:
explicit accounts(QObject* parent = 0) : QObject(parent){}
public slots:
void gmailOpen(const QString &msg)
{
QUrl gmailUrl(msg);
QDesktopServices::openUrl(gmailUrl);
}
};
#endif // ACCOUNTS_H
main.cpp:
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml>
#include "accounts.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView *view = new QQuickView;
QObject *gmail = view->rootObject().findChild<QObject*>("gmailLink");
accounts *gmailAccount = new accounts;
QObject::connect(gmail, SIGNAL(gmailSignal(QString)),gmailAccount,SLOT(gmailOpen(QString)));
view->setSource(QUrl::fromLocalFile("/Users/yudelin/Documents/MyCrazyProjects/Managers4ManyAccounts/main.qml"));
view->show();
return app.exec();
}
main.qml:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
color: "silver"
Image {
id:gmailLink
objectName: "gmailLink"
width: 102
height: 199
fillMode: Image.PreserveAspectFit
source: "...." //the url is too long so I omit it.
anchors.centerIn: parent
signal gmaiSignal (string msg)
MouseArea {
anchors.fill: parent
onClicked:
gmailLink.gmailSignal("http://mail.google.com")
}
}
}
As you can see, I am trying to connect Qml Image with c++ object accounts.
So I use QObject *gmail = view->rootObject().findChild<QObject*>("gmailLink"); to fetch the qml object.
But it does not work. I guess it is suit for the older Qt version.
How could I fix this problem?
You want gmailOpen slot to be called on clicking on the Image. There are simpler and preferred ways of doing it other than doing this.
QObject *gmail = view->rootObject().findChild("gmailLink");
As #Kunal pointed out, you can use Qt.openUrlExternally(string).
If you want to open the Url by using QDesktopServices::openUrl from the gmailOpen slot, you can call the slot directly by setting the context property.
4, QObject *gmail = view->rootObject().findChild<QObject*>("gmailLink");... Here before setting the qml file, you are trying to get a reference for the gmailLink object. Its not created yet.
Following code explains my take on the problem
5a. accounts.h file
#ifndef ACCOUNTS_H
#define ACCOUNTS_H
#include <QObject>
#include <QUrl>
#include <QDesktopServices>
class accounts : public QObject
{
Q_OBJECT
public:
explicit accounts(QObject* parent = 0) : QObject(parent){}
public slots:
void gmailOpen(const QString &msg)
{
QUrl gmailUrl(msg);
QDesktopServices::openUrl(gmailUrl);
}
};
#endif // ACCOUNTS_H
5b. main.cpp file
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml>
#include "accounts.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
accounts *gmailAccount = new accounts;
QQuickView *view = new QQuickView;
view->engine()->rootContext()->setContextProperty("accounts",gmailAccount);
view->setSource(QUrl::fromLocalFile("qml/SO_OpenExternalLink/main.qml"));
view->show();
return app.exec();
}
5c. main.qml file
import QtQuick 2.1
import QtQuick.Dialogs 1.0
Rectangle {
width: 360
height: 360
color: "silver"
Image {
id:gmailLink
objectName: "gmailLink"
width: 102
height: 199
fillMode: Image.PreserveAspectFit
source: "http://upload.wikimedia.org/wikipedia/commons/4/41/Flag_of_India.svg"
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked:Qt.openUrlExternally("http://mail.google.com")
}
}
Image{
id:secondImage
width:102
height:199
fillMode:Image.PreserveAspectFit
source: "http://upload.wikimedia.org/wikipedia/commons/5/55/Emblem_of_India.svg"
anchors.left:gmailLink.right
anchors.top: gmailLink.top
MouseArea {
anchors.fill: parent
onClicked:fileDialog.visible = true
}
}
FileDialog {
id: fileDialog
title: "Please choose a file"
nameFilters: [ "Image files (*.jpg *.png)", "All files (*)" ]
visible:false;
selectMultiple:false
onAccepted:secondImage.source = fileDialog.fileUrl
}
}
5d. You need to change the following from the above code
i. Image source
ii. QML file path in the main.cpp
iii. Possibly import QtQuick 2.0 if you don't have QtQuick 2.1 yet.
If you just want to open URL then you can use Qt.openUrlExternally(string) directly from QML. Here is documentation. No need to create a QObject for this.
And for your code. I don't see any method named gmailSignal.
You should be calling gmailOpen.
like
gmailLink.gmailOpen("http://mail.google.com")

QString exchange in qml through c++

this example is supposed to read the string from TextInput and display it in another Rectangle on click of the mouse. However, it does not. Why?
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QObject>
#include <QString>
class MainWindow : public QDeclarativeView
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};
class Data : public QObject
{
Q_OBJECT
Q_PROPERTY(QString teamName READ getTeamName WRITE setTeamName NOTIFY nameChanged)
public:
Data(QObject *parent = 0);
~Data();
public:
QString getTeamName();
void setTeamName(QString &);
signals:
void nameChanged();
private:
QString n_teamName;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QString>
#include <QObject>
MainWindow::MainWindow(QWidget *parent)
: QDeclarativeView(parent)
{
}
MainWindow::~MainWindow()
{
}
Data::Data(QObject *parent) : QObject(parent)
{
//n_teamName = "Ahoj";
}
Data::~Data(){
}
QString Data::getTeamName(){
return n_teamName;
}
void Data::setTeamName(QString &newName){
if(newName != n_teamName){
n_teamName = newName;
emit nameChanged();
}
}
//main.cpp
#include "mainwindow.h"
#include <qdeclarative.h>
#include <QApplication>
#include <QDeclarativeView>
#include <QDeclarativeContext>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
Data d;
qmlRegisterType<Data>("NData", 1, 0, "Data");
w.rootContext()->setContextProperty("data", &d);
w.setSource(QUrl::fromLocalFile("../klik/Main.qml"));
w.show();
return a.exec();
}
//main.qml
// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
import QtQuick 1.1
import NData 1.0
Rectangle {
id: root
width: 600
height: 400
Rectangle{
id: text
...
TextInput{
anchors.fill: text
anchors.margins: 3
focus: true
}
}
Rectangle{
...
Text{
id: copyText
anchors.centerIn: copy
text: data.setTeamName()
}
}
Rectangle{
id: klik
...
MouseArea{
...
onClicked: {
copyText.text = data.teamName
}
}
}
}
It lets out the error: TypeError: Result of expression 'data.setTeamName' [undefined] is not a function.
Main.qml:51: Error: Cannot assign [undefined] to QString
teamName is a property of data, so simply use assign operator like data.teamName = 'some text';
Probably you should add id field to your TextInput like
TextInput{
id: textInput
anchors.fill: text
anchors.margins: 3
focus: true
}
And change onClicked, so
MouseArea{
...
onClicked: {
data.teamName = textInput.text
copyText.text = data.teamName // copyText.text = textInput.text is fine too
}
}
I believe your actual exception is due to the following line
text: data.setTeamName()
setTeamName is a private function on your data object, that is not exposed as a slot, and is thus undefined when called and assigned to text. Nevermind the fact that it makes no sense to call the set when you are performing an assignment. I'm assuming this was supposed to be data.getTeamName().