Ich versuche eine modifizierte Version des Beispiels "Qmlscatter" zu entwickeln, die in Qt Beispielen verfügbar ist. Ich beabsichtige, eine Flugbahn in der 3D-Umgebung zu zeichnen, indem ich die Datenpunkte der Datei "Data.qml" modifiziere. Die tatsächlichen Koordinaten des Flugweges sind in drei verschiedenen QV-Sektoren QVector<double>
"cogPosX_", "cogPosY_" und "cogPosZ_" gespeichert, wobei jeder Index einen späteren Zeitschritt darstellt.Lade Daten von C++ in eine QML Scatter3d Item
Wie die Foren erwähnen, muss ich die ".setContextProperty" -Funktion verwenden, um die Datenwerte in der QML-Datei zu aktualisieren. Ich finde jedoch nicht wirklich heraus, wie es funktioniert. Ich denke der Hauptgrund ist, dass ich nicht auf die richtige "dataModel" ID verweise. Hier haben Sie meinen Code:
datapointobject.h // Dies sollte ein generisches Objekt definieren, das X Y und Z-Koordinaten für jeden Datenpunkt enthält.
#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 // Dies definiert die Konstruktoren und Funktionen.
#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 // dieser Header definiert die "Flightviewer" Klasse, die eine QQuickView Instanz erstellt, die verantwortlich für die 3D-Scatter Plotten.
#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 // Diese Datei konfiguriert die QQuickView viewer
Instanz. Es ist verantwortlich für den Import der Flugdaten in die QML-Datendatei.
#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();
}
Der 'QQuickViewer Betrachter ist in einer äußeren Funktion initialisiert, mit dem folgenden Befehl:
void Form::run3D(){
threeDviewer= new Flightviewer(displayedFlights_[flightIndex],directory_);//initiate viewer
threeDviewer->showWindow();//Show viewer
}
Die Daten in der qml Datei definiert ist genau wie in dem "qmlscatter" Beispiel:
Data.qml:
import QtQuick 2.1
Item {
property alias model: dataModel
ListModel {
id: dataModel
//ListElement{ xPos: -1000.0; yPos: 500.0; zPos: -5.0 }
}
}
die Daten durch die main.qml Datei zugegriffen wird, die definiert, das Scatter3D Item:
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);
}
}
}
Ich wäre sehr dankbar, wenn Sie mir einen Hinweis auf, wie man programmiert dies richtig geben können. Danke im Voraus!
Ich denke, man echte Datenmodell bieten sollte, abgeleitet von 'QAbstractItemModel' nicht eine Liste von Ihre Datenobjekte (siehe [hier] (https://doc.qt.io/qt-5/qtdatavisualization-data-handling.html#item-models-and-data-mapping)) Dies wird Ihnen helfen, mit zu interagieren das Modell, d. Data Change Notifier etc. Außerdem müssen Sie Ihr 'DataObject' mit' qmlRegisterType' registrieren. QML weiß nichts über Typen, die Sie in C++ definiert haben. – folibis