2017-08-28 2 views
0

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!

+0

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

Antwort

0

ist hier einfaches Beispiel von Daten zu Scatter3D von C++ bereitgestellt:

Firs aller wir ein Modell Klasse von QAbstractListModel abgeleitet definieren. In diesem Fall müssen wir Implementierungen von nur 3 Methoden bereitstellen.

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

Der Konstruktor erstellt Array von 100-200 zufälligen 3D-Punkte als unsere Datenquelle.Um das Modell in QML zu verwenden, sollten wir entweder qmlRegisterType oder setContextProperty verwenden. In diesem Fall benutze ich den ersten.

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

Und nun können wir das Modell in QML verwenden:

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

Danke folibis! Ich habe versucht, Ihre Lösung in meinem Programm zu integrieren, aber ich habe ein Problem mit einem Übersetzungsfehler aufweist: C2280: ‚QQmlPrivate :: QQmlElement :: QQmlElement (void)‘: Sie versuchen, eine gelöschte Funktion mit zu referenzieren [ T = Flugmodell ] Könnten Sie mir bitte dabei helfen? –

+0

Also habe ich festgestellt, dass das Problem auf der "Neudefinition" liegt, die ich für den FlightModel-Konstruktor gemacht habe, der den Flight DisplayFlight importieren musste, um die Trajektoriepunkte zu laden. Das Hinzufügen einer Eingabe zum Konstruktor ist jedoch für den qmlRegisterType nicht gültig, da sein Konstruktor nicht so einfach ist. Gibt es eine Möglichkeit, die Flugdaten in die m_innerData außerhalb der Klasse zu laden? Ich habe versucht, die Variable als statisch zu definieren, aber es scheint nicht zu funktionieren .... Danke –

+0

Normalerweise müssen Sie einige Datenquelle/Anbieter für Ihr Modell definieren. Lesen Sie über MVC in Qt. Außerdem gibt es viele Beispiele im Internet. – folibis