Hi !
I have an issue deleting and replacing 3Dscatter and 3DSurface plots in Qt in C++. I'm working with QtDataVisualization in C++ and I've been stuck for half a year with the following problem : when I try to replace a Q3DScatter graph A by a Q3DScatter graph B, the deletion of graph A makes graph B unreadable if B is built before A is destroyed.
I'm trying to figure out at which point graph A and graph B interact with each other while they are not supposed to. I have a sample code here that is close to a MWE. I have let the function "build" in, but what it does is just initializing the graph and storing its address in a pointer, m3DGraph.
If PROBLEM is set to false, deletion step and creation of the next data are exchanged, and the issue does not happen, but it's not what I'm looking for since I want to update my display once the data is computed, and to be able to change the type of graph in the m3DImageHolder.
I think it's worth mentionning that I've done the test with a Q3DSurface graph and there is a similar issue : with Q3DScatter, the graph doesn't display, with Q3DSurface, the grid disappears.
I already posted this to StackOverflow, but did not get any answer. I link the question here in case there are further discussions Stack Overflow Question
Here is the header:
#include <QDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QVBoxLayout>
#include <QtDataVisualization/Q3DScatter>
#include <QtDataVisualization/Q3DSurface>
namespace QtD = QtDataVisualization;
private:
QtD::Q3DScatter *m3DGraph;
QtD::Q3DSurface *m3DGraphSurface;
public:
enum TYPE{HIDE_BOTH,SCATTER,SURF};
ViewerTest();
void buildScatter(){manageGraphs(TYPE::SCATTER);};
void buildSurface(){manageGraphs(TYPE::SURF);};
void hideAll(){manageGraphs();};
void build(TYPE type);
void manageGraphs(TYPE type=HIDE_BOTH); //Shows or hide graphs
};
#include <QDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QVBoxLayout>
#include <QtDataVisualization/Q3DScatter>
#include <QtDataVisualization/Q3DSurface>
namespace QtD = QtDataVisualization;
class ViewerTest : public QDialog{
private:
QPushButton *mHide,*mNewScat,*mNewSurf;
QVBoxLayout *m3DLayoutHolder;
QHBoxLayout *mButtonsLayout;
QVBoxLayout *mMainLayout;
QWidget *m3DImageHolder;
QtD::Q3DScatter *m3DGraph;
QtD::Q3DSurface *m3DGraphSurface;
public:
enum TYPE{HIDE_BOTH,SCATTER,SURF};
ViewerTest();
void buildScatter(){manageGraphs(TYPE::SCATTER);};
void buildSurface(){manageGraphs(TYPE::SURF);};
void hideAll(){manageGraphs();};
void build(TYPE type);
void manageGraphs(TYPE type=HIDE_BOTH); //Shows or hide graphs
};
To copy to clipboard, switch view to plain text mode
Here is the .cpp
#include "test3DvisuData.h"
#include <QPushButton>
#include <iostream>
#include <QtDataVisualization/qcustom3dlabel.h>
#include <QtDataVisualization/q3dscatter.h>
#include <QtDataVisualization/q3dsurface.h>
#include <QtDataVisualization/QCustom3DVolume>
#include <QtDataVisualization/Q3DInputHandler>
#include <QtDataVisualization/QHeightMapSurfaceDataProxy>
#include <QApplication>
#define PROBLEM true
/// Short class for debugging objects creation
template<typename T> class GraphTest: public T{
const int mLabel;
static int label;
std::string name;
public:
GraphTest(const std::string & in):T(),mLabel(label++),name(in){
std::cout<<" ctor "<< name <<mLabel<< std::endl;
};
~GraphTest(){
std::cout<<" ~dtor "<< name << mLabel<<std::endl;
};
};
template <typename T>int GraphTest<T>::label = 0;
ViewerTest::ViewerTest():m3DImageHolder(nullptr),m3DGraph(nullptr),m3DGraphSurface(nullptr){
setLayout(mMainLayout);
mMainLayout->addLayout(mButtonsLayout);
mMainLayout->addLayout(m3DLayoutHolder);
mButtonsLayout->addWidget(mHide);
mButtonsLayout->addWidget(mNewScat);
mButtonsLayout->addWidget(mNewSurf);
connect(mNewSurf,&QPushButton::released,this,&ViewerTest::buildSurface);
connect(mNewScat,&QPushButton::released,this,&ViewerTest::buildScatter);
connect(mHide,&QPushButton::released,this,&ViewerTest::hideAll);
}
///This part is just the data construction
void ViewerTest::build(TYPE type) {
if(type==SCATTER) {
m3DGraph = new /*QtD::Q3DScatter();*/GraphTest<QtD::Q3DScatter>("inData3D");
m3DGraph->scene()->activeCamera()->setCameraPreset( QtD::Q3DCamera::CameraPresetIsometricRight );
m3DGraph->setOrthoProjection(true);
QtD::QCustom3DVolume *vol = new QtD::QCustom3DVolume;
vol->setScaling(
QVector3D(m3DGraph->axisX()->max() - m3DGraph->axisX()->min(),
(m3DGraph->axisY()->max() - m3DGraph->axisY()->min()) * 1.0f,
m3DGraph->axisZ()->max() - m3DGraph->axisZ()->min()));
vol->setScalingAbsolute(false);
// Build the data
unsigned int height(20),width(20),depth(20);
QVector<uchar> *vectorData = new QVector<uchar>(height * width * depth * 4, 0);
vol->setTextureWidth(width);vol->setTextureHeight(height);vol->setTextureDepth(depth);
vol
->setTextureFormat
(QImage::Format_ARGB32);
for (int z = 0; z < height; z++) {
for (int y = 0; y < depth; y++) {
for (int x = 0; x < width; x++) {
for( int c = 0; c<4; c++){
(*vectorData)[(x + width * z + width * height * y) * 4 + c] =
c==0?x*5:(c==1?y*5:(c==2?z*5:20));
}
}
}
}
vol->setTextureData(vectorData);
m3DGraph->addCustomItem(vol);
}
///// ------------------
else if(type==SURF){
for(uint x=0; x<20; x++){
for(uint y=0; y<20; y++){
image.
setPixelColor(x,y,
QColor(x
*y
));
}
}
QtD::QHeightMapSurfaceDataProxy *proxy = new QtD::QHeightMapSurfaceDataProxy(image);
QtD::QSurface3DSeries *serie = new QtD::QSurface3DSeries(proxy);
serie->setDrawMode(QtD::QSurface3DSeries::DrawSurface);
m3DGraphSurface = new GraphTest<QtD::Q3DSurface>("inData3DSurface");
m3DGraphSurface->addSeries(serie);
m3DGraphSurface->seriesList().at(0)->setColorStyle( QtD::Q3DTheme::ColorStyleRangeGradient );
}
}
#include "test3DvisuData.h"
#include <QPushButton>
#include <iostream>
#include <QtDataVisualization/qcustom3dlabel.h>
#include <QtDataVisualization/q3dscatter.h>
#include <QtDataVisualization/q3dsurface.h>
#include <QtDataVisualization/QCustom3DVolume>
#include <QtDataVisualization/Q3DInputHandler>
#include <QtDataVisualization/QHeightMapSurfaceDataProxy>
#include <QApplication>
#define PROBLEM true
/// Short class for debugging objects creation
template<typename T> class GraphTest: public T{
const int mLabel;
static int label;
std::string name;
public:
GraphTest(const std::string & in):T(),mLabel(label++),name(in){
std::cout<<" ctor "<< name <<mLabel<< std::endl;
};
~GraphTest(){
std::cout<<" ~dtor "<< name << mLabel<<std::endl;
};
};
template <typename T>int GraphTest<T>::label = 0;
ViewerTest::ViewerTest():m3DImageHolder(nullptr),m3DGraph(nullptr),m3DGraphSurface(nullptr){
mHide = new QPushButton("hide",this);
mNewScat = new QPushButton("scat",this);
mNewSurf = new QPushButton("surf",this);
m3DLayoutHolder = new QVBoxLayout();
mButtonsLayout = new QHBoxLayout();
mMainLayout = new QVBoxLayout(this);
setLayout(mMainLayout);
mMainLayout->addLayout(mButtonsLayout);
mMainLayout->addLayout(m3DLayoutHolder);
mButtonsLayout->addWidget(mHide);
mButtonsLayout->addWidget(mNewScat);
mButtonsLayout->addWidget(mNewSurf);
connect(mNewSurf,&QPushButton::released,this,&ViewerTest::buildSurface);
connect(mNewScat,&QPushButton::released,this,&ViewerTest::buildScatter);
connect(mHide,&QPushButton::released,this,&ViewerTest::hideAll);
}
///This part is just the data construction
void ViewerTest::build(TYPE type) {
if(type==SCATTER) {
m3DGraph = new /*QtD::Q3DScatter();*/GraphTest<QtD::Q3DScatter>("inData3D");
m3DGraph->scene()->activeCamera()->setCameraPreset( QtD::Q3DCamera::CameraPresetIsometricRight );
m3DGraph->setOrthoProjection(true);
QtD::QCustom3DVolume *vol = new QtD::QCustom3DVolume;
vol->setScaling(
QVector3D(m3DGraph->axisX()->max() - m3DGraph->axisX()->min(),
(m3DGraph->axisY()->max() - m3DGraph->axisY()->min()) * 1.0f,
m3DGraph->axisZ()->max() - m3DGraph->axisZ()->min()));
vol->setScalingAbsolute(false);
// Build the data
unsigned int height(20),width(20),depth(20);
QVector<uchar> *vectorData = new QVector<uchar>(height * width * depth * 4, 0);
vol->setTextureWidth(width);vol->setTextureHeight(height);vol->setTextureDepth(depth);
vol->setTextureFormat(QImage::Format_ARGB32);
for (int z = 0; z < height; z++) {
for (int y = 0; y < depth; y++) {
for (int x = 0; x < width; x++) {
for( int c = 0; c<4; c++){
(*vectorData)[(x + width * z + width * height * y) * 4 + c] =
c==0?x*5:(c==1?y*5:(c==2?z*5:20));
}
}
}
}
vol->setTextureData(vectorData);
m3DGraph->addCustomItem(vol);
}
///// ------------------
else if(type==SURF){
QImage image = QImage(20,20,QImage::Format_Grayscale8);
for(uint x=0; x<20; x++){
for(uint y=0; y<20; y++){
image.setPixelColor(x,y,QColor(x*y));
}
}
QtD::QHeightMapSurfaceDataProxy *proxy = new QtD::QHeightMapSurfaceDataProxy(image);
QtD::QSurface3DSeries *serie = new QtD::QSurface3DSeries(proxy);
serie->setDrawMode(QtD::QSurface3DSeries::DrawSurface);
m3DGraphSurface = new GraphTest<QtD::Q3DSurface>("inData3DSurface");
m3DGraphSurface->addSeries(serie);
m3DGraphSurface->seriesList().at(0)->setColorStyle( QtD::Q3DTheme::ColorStyleRangeGradient );
}
}
To copy to clipboard, switch view to plain text mode
and here is the problematic function
void ViewerTest::manageGraphs(TYPE type) {
#if PROBLEM
build(type); //---> no error thrown, but graph don't display
#endif
// delete previous
m3DLayoutHolder->removeWidget(m3DImageHolder);
delete m3DImageHolder;
m3DImageHolder= nullptr;
#if PROBLEM
#else
build(type); ///---> display works perfectly
#endif
if(type == SCATTER) {
m3DImageHolder
= QWidget::createWindowContainer(m3DGraph,
this);
} else if(type==SURF){
m3DImageHolder
= QWidget::createWindowContainer(m3DGraphSurface,
this);
}
if(type != TYPE::HIDE_BOTH) {
m3DLayoutHolder->addWidget(m3DImageHolder);
}
}
// Just a Widget with two buttons (hide/new)
// OnClick, load a new3D dataset
int main(int argc, char** argv){
ViewerTest viewerTest;
return viewerTest.exec();
}
void ViewerTest::manageGraphs(TYPE type) {
#if PROBLEM
build(type); //---> no error thrown, but graph don't display
#endif
// delete previous
m3DLayoutHolder->removeWidget(m3DImageHolder);
delete m3DImageHolder;
m3DImageHolder= nullptr;
#if PROBLEM
#else
build(type); ///---> display works perfectly
#endif
if(type == SCATTER) {
m3DImageHolder = QWidget::createWindowContainer(m3DGraph, this);
} else if(type==SURF){
m3DImageHolder = QWidget::createWindowContainer(m3DGraphSurface,this);
}
if(type != TYPE::HIDE_BOTH) {
m3DLayoutHolder->addWidget(m3DImageHolder);
m3DImageHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
}
// Just a Widget with two buttons (hide/new)
// OnClick, load a new3D dataset
int main(int argc, char** argv){
QApplication qApplication( argc, argv);
ViewerTest viewerTest;
return viewerTest.exec();
}
To copy to clipboard, switch view to plain text mode
Bookmarks