How to avoid crash in QQuickView? - qt

Are there any ways to correclty close QQuickView without crashes? I posted a bug to Digia, but there still no response :(
QML:
import QtQuick 2.2
import QtQuick.Controls 1.2
Rectangle
{
id: root;
signal quit();
Component.onDestruction: quit();
BusyIndicator
{
id: progress
running: true
anchors.fill: parent
}
}
CPP:
#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>
#include <QPointer>
int main(int argc, char *argv[] )
{
QGuiApplication a( argc, argv );
QPointer< QQuickView > w = new QQuickView();
QObject::connect( w, &QQuickView::statusChanged, [=]()
{
QObject::connect( w->rootObject(), SIGNAL( quit() ), qApp, SLOT( quit() ) );
} );
w->setSource( QUrl( "qrc:/test.qml" ) );
w->setResizeMode( QQuickView::SizeRootObjectToView );
w->show();
a.exec();
return 0;
}

Related

Update a MapCircle on QML using a signal from C++

I'm trying to update a MapCircle in QML from a signal in C++ and I'm veen having several issues with it all day.
In my class I have a Q_PROPERTY which is read only and holds the GPS positions of 4 UAVs in a QVariantList
class GCS: public QObject
{
Q_PROPERTY(QVariantList getUavPosition READ getUavPosition NOTIFY uavPositionSet)
public:
QVariantList getUavPosition() ;
signals:
void uavPositionSet();
public slots:
void setUavPosition();
void triggerPosition();
private:
QVariantList connected_uavs;
QVector<QGeoCoordinate> uav_positions;
};
I then define the functions as:
void GCS::setUavPosition()
{
double i = 0.0;
QGeoCoordinate uav_id;
uav_id.setLatitude(0.5);
uav_id.setLongitude(0.5 + i);
uav_id.setAltitude(5);
uav_positions.insert(0, uav_id);
connected_uavs.append( QVariant::fromValue(QGeoCoordinate(uav_positions[0].latitude(), uav_positions[0].longitude())));
i+=0.15;
emit uavPositionSet();
}
QVariantList GCS::getUavPosition()
{
return connected_uavs;
}
void GCS::triggerPosition()
{
setUavPosition();
ROS_INFO("Pos trig");
}
In my main function, I connect triggerPosition to a Timer so as to update the position periodically
int main(int argc, char *argv[])
{
ros::init(argc, argv, "planner");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
GCS gcs;
context->setContextProperty("planner", &gcs);
engine.load(QUrl(QStringLiteral("qrc:/planner.qml")));
QTimer *timer = new QTimer();
timer->setInterval(1000);
QObject::connect(&gcs, SIGNAL(uavPositionSet()), &gcs, SLOT(setUavPosition()));
QObject::connect(timer, SIGNAL(timeout()), &gcs, SLOT(triggerPosition()));
timer->start();
return app.exec();
}
However, when I run my program, there's a slight delay, my mouseArea becomes unusable and the program crashes. When I try to print the longitude to see if it updates, The initial value is printed out multiple times to the terminal but then the program crashes and there's is no MapCircle present on the map
The relevant part of My Qml file looks like this:
Map{
id: map
anchors.fill:parent
plugin: mapPlugin
center: QtPositioning.coordinate(0.5, 0.5)
zoomLevel:50
MapCircle{
id:uavPos
radius:2
color:'black'
border.width:3
}
Connections{
id:uavConnection
target: planner
onUavPositionSet:{
var data = planner.getUavPosition
uavPos.center = QtPositioning.coordinate(data[0].latitude, data[0].longitude)
console.log(data[0].longitude)
}
}
}
Can someone please kindly point me in the right direction here?
If you are going to handle information from several elements then it is better to use a model (together with a Repeater to create several elements), so it is only necessary to modify the role of an item:
gcs.h
#ifndef GCS_H
#define GCS_H
#include <QObject>
class QStandardItemModel;
class QAbstractItemModel;
class GCS: public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* uavModel READ uavModel CONSTANT)
public:
enum UAVRoles {
PositionRole = Qt::UserRole + 1000
};
GCS(QObject *parent=nullptr);
QObject *uavModel() const;
public Q_SLOTS:
void triggerPosition();
private:
QStandardItemModel* m_uavModel;
};
#endif // GCS_H
gcs.cpp
#include "gcs.h"
#include <QGeoCoordinate>
#include <QStandardItemModel>
#include <random>
GCS::GCS(QObject *parent):
QObject(parent), m_uavModel(new QStandardItemModel(this))
{
m_uavModel->setItemRoleNames({{PositionRole, "position"}});
for(int i =0; i < 4; i++){
QStandardItem *item = new QStandardItem;
item->setData(QVariant::fromValue(QGeoCoordinate()), PositionRole);
m_uavModel->appendRow(item);
}
}
QObject *GCS::uavModel() const{
return m_uavModel;
}
void GCS::triggerPosition(){
std::mt19937 rng;
rng.seed(std::random_device()());
std::normal_distribution<> dist(-0.0001, +0.0001);
if(QStandardItem *item = m_uavModel->item(0)){
QGeoCoordinate uav_id;
uav_id.setLatitude(0.5 + dist(rng));
uav_id.setLongitude(0.5 + dist(rng));
uav_id.setAltitude(5);
item->setData(QVariant::fromValue(uav_id), PositionRole);
}
}
main.cpp
#include "gcs.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
GCS gcs;
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
context->setContextProperty("planner", &gcs);
const QUrl url(QStringLiteral("qrc:/planner.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);
QTimer timer;
timer.setInterval(1000);
QObject::connect(&timer, &QTimer::timeout, &gcs, &GCS::triggerPosition);
timer.start();
return app.exec();
}
planner.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtLocation 5.14
import QtPositioning 5.14
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Plugin {
id: mapPlugin
name: "osm"
}
Map{
id: map
anchors.fill:parent
plugin: mapPlugin
center: QtPositioning.coordinate(0.5, 0.5)
zoomLevel:50
MapItemView{
model: planner.uavModel
delegate: MapCircle{
id:uavPos
radius: 2
color:'black'
border.width:3
center: QtPositioning.coordinate(model.position.latitude, model.position.longitude)
}
}
}
}

What is the most formal way to invoke C++ code in response to a QML button?

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)
}
}

Deleting QQuickView on exit cause Qt application to freeze

I founded a deadlock in Qt 5.3. How to fix in correctly? Without ugly fix (not to delete qquickview *)
I have a singleton with a pointer to QQuickView. When I need to close my application I do a call of QGuiApplication::quit() and try to release QQuickView * in destructor of singlenot. Result - application freezes.
Sample:
test.qml
import QtQuick 2.1
Rectangle
{
id: root;
color: "black";
signal quit();
Component.onDestruction: quit();
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>
#include <QPointer>
struct Singleton
{
QPointer< QQuickView > w;
static Singleton inst;
int run( int argc, char *argv[] )
{
QGuiApplication a( argc, argv );
w = new QQuickView();
QObject::connect( w, &QQuickView::statusChanged, [=]()
{
QObject::connect( w->rootObject(), SIGNAL( quit() ), qApp, SLOT( quit() ) );
} );
w->setSource( QUrl( "qrc:/test.qml" ) );
w->setResizeMode( QQuickView::SizeRootObjectToView );
w->show();
a.exec();
return 0;
}
~Singleton()
{
delete w; // Comment this to fix bug
}
};
Singleton Singleton::inst;
int main(int argc, char *argv[] )
{
Singleton::inst.run( argc, argv );
return 0;
}
P.S. C++0x is used for simplifying code. Same result on C++03 compilers.
It was a bug in Qt. Fixed since 5.4 version.

Capture QML drawing buffer, without displaying

I need to grab each QML (QtQuick 2) drawing frame and sent it over the network.
At the moment I have used method listed below, but this method has two big disadvantage
1) Due to Qt5 documentation grabWindow() function has performance issues
2) It can't work with hidden QML window
Is it possible to get OpenGL render buffer right after QQuickWindow::afterRendering ?
Using FBOs ? Shared opengl context ?
class Grab: public QObject
{
public:
Grab( QQuickWindow * wnd ) : wnd_(wnd) {}
public slots:
void Grabme()
{
QImage image = wnd_->grabWindow();
}
private:
QQuickWindow *wnd_;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();
Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
&grab, &Grab::Grabme, Qt::DirectConnection );
return app.exec();
}
Example bellow can grab any qml content to FBO and then sent it as Image via signal.
Only one problem of this approach is visibility, grab window must be visible for successful grabbing. If anybody knows how to prevent this you can help me and provide more advanced approach.
// main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
GrabWindow grab;
grab.setResizeMode( QQuickView::SizeViewToRootObject );
grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
grab.setFlags( Qt::Popup );
grab.show();
return app.exec();
}
// grabwindow.hpp
#pragma once
#include <QOpenGLFramebufferObject>
#include <QScopedPointer>
#include <QQuickView>
#include <QImage>
class GrabWindow: public QQuickView
{
Q_OBJECT
signals:
void changeImage( const QImage &image );
public:
GrabWindow( QWindow * parent = 0 );
private slots:
void afterRendering();
void beforeRendering();
private:
QScopedPointer<QOpenGLFramebufferObject> fbo_;
};
// grabwindow.cpp
#include "grabwindow.hpp"
#include <limits>
GrabWindow::GrabWindow( QWindow * parent ) :
QQuickView( parent )
{
setClearBeforeRendering( false );
setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
connect( this, SIGNAL( afterRendering() ), SLOT( afterRendering() ), Qt::DirectConnection );
connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
}
void GrabWindow::afterRendering()
{
if( !fbo_.isNull() )
{
emit changeImage( fbo_->toImage() );
}
}
void GrabWindow::beforeRendering()
{
if (!fbo_)
{
fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
setRenderTarget(fbo_.data());
}
}
I managed to find a trick to make grabWindow() work when the Window is "not visible". The trick is to set the window's visibility: Window.Minimized and the flags: Qt.Tool. The window is not displayed to the user, but to the Qt's internals it appears to be visible and the grabWindow() method call works as expected. Remember to call that method only once the scene has been initialised.
The only problem with this solution (that I have come across) is that if the window's color property is set to transparent, the captured content has black background.
With later versions of Qt 5.X you can also use the software render backend.
The following renders any scene in the background without any visible window or OpenGL tricks:
// main.cpp
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
int main(int argc, char *argv[])
{
const char *source = "qrc:/main.qml";
if (argc > 1) source = argv[1];
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QGuiApplication app{argc, argv};
QQuickRenderControl renderControl;
QQuickWindow window{&renderControl};
QQmlEngine engine;
QQmlComponent component{
&engine,
QUrl{QString::fromUtf8(source)}
};
QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
window.contentItem()->setSize(rootItem->size());
rootItem->setParentItem(window.contentItem());
window.resize(rootItem->size().width(), rootItem->size().height());
QImage image = renderControl.grab();
image.save("output.png");
return 0;
}

qt: How to animate the transparency of a QDeclarativeView using QPropertyAnimation?

I want to use QPropertyAnimation to animate the transparency of a QDeclarativeView .But,it doesn't work.It's nothing to change and no errors, but I use property "geometry",it works.I have no ideas.
Here is my main.cpp code:
int main(int argc, char *argv[])
{
QApplication a(argc, argv, true);
QDeclarativeView* view;
QDeclarativeContext *context;
QDeclarativeEngine* engine;
Connector* connector;
view = new QDeclarativeView();
connector = new Connector();
context = view->rootContext();
context->setContextProperty("Connector", connector);
context->setContextProperty("gRadioQMLDir", QDir::currentPath());
view->setSource(QUrl::fromLocalFile("qml/Main.qml"));
view->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
view->show();
QPropertyAnimation animation(view, "windowOpacity");
animation.setDuration(30000);
animation.setStartValue(0.0);
animation.setEndValue(1.0);
animation.start();
return a.exec();
}
Try to animate widget with using QGraphicsOpacityEffect. Sample:
#include <QApplication>
#include <QWidget>
#include <QDeclarativeView>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QPushButton>
int main( int argc, char *argv[] )
{
QApplication a( argc, argv );
QWidget w;
QGraphicsOpacityEffect *opacity = new QGraphicsOpacityEffect;
QPropertyAnimation *anim = new QPropertyAnimation( opacity, "opacity" );
//QDeclarativeView *dv = new QDeclarativeView( &w );
//dv->setFixedSize( 200, 200 );
//dv->setGraphicsEffect( opacity );
QPushButton *btn = new QPushButton( "Button", &w );
btn->setGraphicsEffect( opacity );
anim->setDuration( 2000 );
anim->setStartValue( 0.1 );
anim->setEndValue( 1.0 );
anim->setEasingCurve( QEasingCurve::InCubic );
anim->start();
w.setFixedSize( 300, 200 );
w.show();
return a.exec();
}
Note: "windowOpacity" property is valid only for widgets that are windows.

Resources