qt — загрузка данных из c ++ в элемент QML Scatter3d

Я пытаюсь разработать модифицированную версию примера «qmlscatter», доступную в примерах Qt. Мое намерение состоит в том, чтобы построить траекторию полета в 3D-среде, изменив точки данных в файле «Data.qml». Фактические координаты траектории полета хранятся в трех разных QVectors QVector<double> «cogPosX_», «cogPosY_» и «cogPosZ_», каждый индекс представляет собой последний шаг по времени.

Как упоминается на форумах, мне нужно использовать функцию .setContextProperty для обновления значений данных в файле QML. Тем не менее, я не могу найти действительно узнать, как заставить его работать. Я предполагаю, что основная причина в том, что я не ссылаюсь на правильный идентификатор «dataModel». Здесь у вас есть мой код:

datapointobject.h // Это должно определить универсальный объект, который содержит координаты X Y и Z для каждой точки данных.

#ifndef DATAPOINTOBJECT_H
#define DATAPOINTOBJECT_H
#include <QWidget>
class DataObject : public QObject
{
Q_OBJECT

Q_PROPERTY(double x READ x WRITE setX)
Q_PROPERTY(double y READ y WRITE setY)
Q_PROPERTY(double z READ z WRITE setZ)
public:
DataObject(QObject* parent =0);//Constructor
DataObject(const double & x, const double & y, const double & z, QObject * parent=0);
~DataObject();//Destructor
double x() const;
double y() const;
double z() const;
void setX(const double &x);
void setY(const double &y);
void setZ(const double &z);
signals:

void xChanged();
void yChanged();
void zChanged();
private:
double m_x;
double m_y;
double m_z;
};
#endif // DATAPOINTOBJECT_H

datapointobject.cpp // Это определяет конструкторы и функции.

#include "datapointobject.h"#include<QDebug>

DataObject::DataObject(QObject *parent)//Constructor
: QObject(parent)
{
}
DataObject::DataObject(const double &x, const double &y, const double &z, QObject *parent)//Constructor
:QObject(parent), m_x(x), m_y(y), m_z(z)
{

}

DataObject::~DataObject(){//Destructor

}
double DataObject::x() const
{
return m_x;
}
double DataObject::y() const
{
return m_y;
}
double DataObject::z() const
{
return m_z;
}
void DataObject::setX(const double &x){
if (x != m_x) {
m_x = x;
emit xChanged();
}
}
void DataObject::setY(const double &y){
if (y != m_y) {
m_y = y;
emit yChanged();
}
}
void DataObject::setZ(const double &z){
if (z != m_z) {
m_z = z;
emit zChanged();
}
}

threeDviewer.h // этот заголовок определяет класс «Flightviewer», который создает экземпляр QQuickView, отвечающий за построение трехмерного разброса.

#ifndef FLIGHTVIEWER_H
#define FLIGHTVIEWER_H
#include <QtWidgets>
#include <QtGui/QGuiApplication>
#include <QtCore/QDir>
#include <QtQuick/QQuickView>
#include <QtQml/QQmlEngine>
#include "flight.h"#include <QtQml>

class Flightviewer: public QWidget
{
Q_OBJECT

public:
Flightviewer(Flight displayedFlight, QString directory, QWidget *parent = 0);//Constructor
virtual ~Flightviewer();//Destructor
QQuickView viewer;

void showWindow();

void readFlightTrajectory(Flight flight);
};#endif // FLIGHTVIEWER_H

threeDviewer.cpp // Этот файл настраивает QQuickView viewer пример. Он отвечает за импорт данных полета в файл данных QML.

#include <QtGui/QGuiApplication>
#include <QtCore/QDir>
#include <QtQuick/QQuickView>
#include <QtQml/QQmlEngine>

#include "window.h"#include <QWidget>
#include "threeDviewer.h"#include "datapointobject.h"Flightviewer::Flightviewer(Flight displayedFlight, QString directory, QWidget*parent){

// The following are needed to make examples run without having to install the module
// in desktop environments.
#ifdef Q_OS_WIN
QString extraImportPath(QStringLiteral("%1/../../../../%2"));
#else
QString extraImportPath(QStringLiteral("%1/../../../%2"));
#endif
qmlRegisterType<DataObject>();
readFlightTrajectory(displayedFlight);
viewer.setVisible(false);//Open only after clicked.
viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
QString::fromLatin1("qml")));
//! [4]
QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close);
//! [4]

viewer.setTitle(QStringLiteral("3D Flight Trajectory Visualization"));

//! [3]
viewer.setSource(QUrl("qrc:/qmlscatter/qml/qmlscatter/main.qml"));
//! [3]

viewer.setResizeMode(QQuickView::SizeRootObjectToView);

//! [2]
//! [2]
}
Flightviewer::~Flightviewer(){

}

void Flightviewer::showWindow(){
viewer.showMaximized();
}
void Flightviewer::readFlightTrajectory(Flight flight){

QList<QObject*> dataList;
for (int i=0; i<flight.cogPosX_.size();i++){
dataList.append(new DataObject(flight.cogPosX_.at(i),flight.cogPosY_.at(i),flight.cogPosZ_.at(i)));
}
QQmlContext * ctxt = viewer.rootContext();
ctxt->setContextProperty("dataModel", QVariant::fromValue(dataList));
viewer.update();
}

«Средство просмотра QQuickViewer» инициализируется во внешней функции следующей командой:

void Form::run3D(){
threeDviewer= new Flightviewer(displayedFlights_[flightIndex],directory_);//initiate viewer

threeDviewer->showWindow();//Show viewer

}

Данные в файле qml определяются точно так же, как в примере «qmlscatter»:

Data.qml:

import QtQuick 2.1
Item {

property alias model: dataModel

ListModel {
id: dataModel
//ListElement{ xPos: -1000.0; yPos: 500.0; zPos: -5.0 }
}
}

Доступ к данным осуществляется через файл main.qml, который определяет элемент Scatter3D:

main.qml

    //! [0]
import QtQuick 2.1
import QtQuick.Layouts 1.0
import QtDataVisualization 1.0
import "."//! [0]

//! [1]
Rectangle {
id: mainView
//! [1]
width: 500
height: 500

//! [4]
Data {
id: seriesData
}
//! [4]

//! [13]
Theme3D {
id: themeIsabelle
type: Theme3D.ThemeIsabelle
font.family: "Lucida Handwriting"font.pointSize: 40
}
//! [13]

Theme3D {
id: themeArmyBlue
type: Theme3D.ThemeArmyBlue
}

//! [8]
//! [9]
Item {
id: dataView
anchors.bottom: parent.bottom
//! [9]
width: parent.width
height: parent.height - buttonLayout.height
//! [8]

//! [2]
Scatter3D {
id: scatterGraph
width: dataView.width
height: dataView.height
//! [2]
//! [3]
theme: themeIsabelle
shadowQuality: AbstractGraph3D.ShadowQualitySoftLow
//! [3]
//! [6]
axisX.segmentCount: 3
axisX.subSegmentCount: 2
axisX.labelFormat: "%.2f"axisZ.segmentCount: 2
axisZ.subSegmentCount: 2
axisZ.labelFormat: "%.2f"axisY.segmentCount: 2
axisY.subSegmentCount: 2
axisY.labelFormat: "%.2f"//! [6]
//! [5]
Scatter3DSeries {
id: scatterSeries
//! [5]
//! [10]
itemLabelFormat: "Series 1: X:@xLabel Y:@yLabel Z:@zLabel"//! [10]

//! [11]
ItemModelScatterDataProxy {
itemModel: seriesData.model
xPosRole: "xPos"yPosRole: "yPos"zPosRole: "zPos"}
//! [11]
}
}
}

RowLayout {
id: buttonLayout
Layout.minimumHeight: cameraToggle.height
width: parent.width
anchors.left: parent.left
spacing: 0
//! [7]
NewButton {
id: shadowToggle
Layout.fillHeight: true
Layout.fillWidth: true
text: scatterGraph.shadowsSupported ? "Hide Shadows" : "Shadows not supported"enabled: scatterGraph.shadowsSupported
onClicked: {
if (scatterGraph.shadowQuality === AbstractGraph3D.ShadowQualityNone) {
scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualitySoftLow;
text = "Hide Shadows";
} else {
scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityNone;
text = "Show Shadows";
}
}
}
//! [7]

NewButton {
id: smoothToggle
Layout.fillHeight: true
Layout.fillWidth: true
text: "Use Smooth for Series One"onClicked: {
if (scatterSeries.meshSmooth === false) {
text = "Use Flat for Series One";
scatterSeries.meshSmooth = true;
} else {
text = "Use Smooth for Series One"scatterSeries.meshSmooth = false;
}
}
}

NewButton {
id: cameraToggle
Layout.fillHeight: true
Layout.fillWidth: true
text: "Change Camera Placement"onClicked: {
if (scatterGraph.scene.activeCamera.cameraPreset === Camera3D.CameraPresetFront) {
scatterGraph.scene.activeCamera.cameraPreset =
Camera3D.CameraPresetIsometricRightHigh;
} else {
scatterGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetFront;
}
}
}

NewButton {
id: themeToggle
Layout.fillHeight: true
Layout.fillWidth: true
text: "Change Theme"onClicked: {
if (scatterGraph.theme.type === Theme3D.ThemeArmyBlue) {
scatterGraph.theme = themeIsabelle
} else {
scatterGraph.theme = themeArmyBlue
}
if (scatterGraph.theme.backgroundEnabled === true) {
backgroundToggle.text = "Hide Background";
} else {
backgroundToggle.text = "Show Background";
}
}
}

NewButton {
id: backgroundToggle
Layout.fillHeight: true
Layout.fillWidth: true
text: "Hide Background"onClicked: {
if (scatterGraph.theme.backgroundEnabled === true) {
scatterGraph.theme.backgroundEnabled = false;
text = "Show Background";
} else {
scatterGraph.theme.backgroundEnabled = true;
text = "Hide Background";
}
}
}

NewButton {
id: exitButton
Layout.fillHeight: true
Layout.fillWidth: true
text: "Quit"onClicked: Qt.quit(0);
}
}
}

Буду очень признателен, если вы подскажете, как правильно программировать это. Заранее спасибо!

0

Решение

Вот простой пример предоставления данных в Scatter3D из C ++:

Во-первых, мы определяем модельный класс, полученный из QAbstractListModel. В этом случае мы должны предоставить реализации только 3 методов.

FlightModel.h

#ifndef FLIGHTMODEL_H
#define FLIGHTMODEL_H

#include <QAbstractListModel>
#include <QList>
#include <QVector3D>

class FlightModel : public QAbstractListModel
{
Q_OBJECT
public:
FlightModel(QObject *parent = Q_NULLPTR);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;

private:
QList<QVector3D> m_innerData;
};

#endif // FLIGHTMODEL_H

FlightModel.cpp

#include "flightmodel.h"
FlightModel::FlightModel(QObject *parent) : QAbstractListModel(parent)
{
qsrand(time(NULL));
int count = 100 + qrand() % 100;
for(int i = 0;i < count;i ++) {
double x = (double)(qrand() % 1000);
double y = (double)(qrand() % 1000);
double z = (double)(qrand() % 1000);
m_innerData.append(QVector3D(x, y, z));
}
}

int FlightModel::rowCount(const QModelIndex &parent) const
{
return m_innerData.count();
}

QVariant FlightModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();

QVector3D point = m_innerData[index.row()];
switch(role) {
case Qt::UserRole + 1: return point.x(); break;
case Qt::UserRole + 2: return point.y(); break;
case Qt::UserRole + 3: return point.z(); break;
}
return QVariant();
}

QHash<int, QByteArray> FlightModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole + 1] = "x";
roles[Qt::UserRole + 2] = "y";
roles[Qt::UserRole + 3] = "z";
return roles;
}

В качестве источника данных конструктор создает массив из 100-200 случайных трехмерных точек.

Чтобы использовать модель в QML, мы должны использовать либо qmlRegisterType или же setContextProperty, В этом случае я использую первый.

QQmlApplicationEngine engine;
qmlRegisterType<FlightModel>("mytest", 1, 0, "FlightModel");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

И теперь мы можем использовать модель в QML:

main.qml

import QtQuick 2.9
import QtQuick.Window 2.0
import QtDataVisualization 1.2
import mytest 1.0

Window {
id: window
width: 600
height: 400
visible: true

FlightModel {
id: dataModel
}

Scatter3D {
anchors.fill: parent
Scatter3DSeries {
ItemModelScatterDataProxy {
itemModel: dataModel

xPosRole: "x"yPosRole: "y"zPosRole: "z"}
}
}
}
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]