2017-08-28 59 views
0

我正在嘗試開發qt示例中提供的示例「qmlscatter」的修改版本。我的意圖是通過修改「Data.qml」文件的數據點來繪製3D環境中的飛行軌跡。飛行路徑的實際座標存儲在三個不同的「cogPosX_」,「cogPosY_」和「cogPosZ_」三個不同的QVectors中,每個索引代表後一個時間步。將數據從C++加載到QML Scatter3d項目

正如論壇提到的,我需要使用「.setContextProperty」函數來更新QML文件中的數據值。但是,我找不到如何讓它工作。我想主要原因是我沒有引用正確的「dataModel」ID。這裏你有我的代碼:

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觀察者」在外部功能初始化,用下面的命令:在QML文件

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

    threeDviewer->showWindow();//Show viewer 

} 

的數據被定義完全如在「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

我想你應該提供真正的DataModel,從'QAbstractItemModel'不是一個列表導出你的數據對象(見[這裏](https://doc.qt.io/qt-5/qtdatavisualization-data-handling.html#item-models-and-data-mapping))這將幫助你與該模型即。數據更改通知等。另外,您必須使用'qmlRegisterType'註冊您的'DataObject'。 QML對你在C++中定義的類型一無所知。 – folibis

回答

0

下面是從C++提供數據給Scatter3D的簡單示例:

所有我們定義從 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 3D點作爲我們的數據源陣列。

要在QML中使用該模型,我們應該使用qmlRegisterTypesetContextProperty。在這種情況下,我使用第一個。

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

謝謝你的幻燈片! 我試圖解決方案在我的節目集成,但我有一個編譯錯誤問題: C2280:「QQmlPrivate :: QQmlElement :: QQmlElement(無效)」:試圖引用刪除功能 與 [ T = FlightModel ] 請問您能幫我解答一下嗎? –

+0

因此,我發現問題在於我爲FlightModel構造函數完成的「重新定義」,該構造函數必須導入Flight displayedFlight以加載軌跡點。但是,向構造函數添加一個輸入對於qmlRegisterType 無效,因爲它的構造函數不是那麼簡單。 有什麼方法可以將航班數據加載到班外的m_innerData上?我已經嘗試將變量定義爲靜態,但它似乎沒有工作....謝謝 –

+0

通常你必須爲你定義一些數據源/提供者模型。閱讀關於Qt中的MVC。互聯網上也有很多例子。 – folibis