PySide6 QtDataVisualization: Q3DSurface Figure Not Showing
-
Hello, I am trying to create a surface plot with the QDataVisualization QSurface3D library and have been running into some issues.
Data:
So, essentially what I am trying to do is get my x, y, and z data to plot a figure using Q3DSurface. I am reading in this data from a file, and converting into into a Pandas dataframe. The values in it consist ofnumpy.float64
data types. Each of these X, Y , and Z columns are of length 446, 082. The intended figure ends up should end up looking like a cylinder.Problem:
I more more or less tried to follow this example on the qt docs website here. The main issue that I am having is that the figure is not showing up. If you look at my output, you can see that all the axes are filled with values, and all of them are accurate. I cross-validated them with my data log and it seems that they are in the right places and axes. The issue is that no figure is being output on the actual plot itself. There is a shade in it, which leads me to believe that it just didnt finish drawing what is supposed to be there.The data is there but I feel like I am missing something crucial that I haven't caught. Maybe the issue is with the drawing or how I am setting the data, but I've been struggling to figure it out. I have tried different formats for surface data item and messing with the data but nothing really seems to work.
Any ideas, guides, or solutions to this would be appreciated. Thank you.
Code:
import sys import pandas as pd from PySide6.QtGui import QVector3D from PySide6.QtWidgets import QApplication, QWidget, QHBoxLayout from PySide6.QtCore import QObject from PySide6.QtDataVisualization import ( QSurface3DSeries, QSurfaceDataItem, QSurfaceDataProxy, QValue3DAxis, Q3DSurface) class Surface(QObject): def __init__(self, surface, parent=None): QObject.__init__(self, parent) self.graph = surface self.graph.setAxisX(QValue3DAxis()) self.graph.setAxisY(QValue3DAxis()) self.graph.setAxisZ(QValue3DAxis()) self.surfaceproxy = QSurfaceDataProxy() self.surfaceseries = QSurface3DSeries(self.surfaceproxy) self.create_data() def create_data(self): file = "mydatalog.txt" vars = ['X [mm]', 'Y [mm]', 'Z [mm]'] df1 = pd.read_csv(file, low_memory=False, on_bad_lines='skip', usecols=vars) df1 = df1.apply(pd.to_numeric) # Each of these are of length, 446082 x = list(df1['X [mm]'].values) y = list(df1['Y [mm]'].values) z = list(df1['Z [mm]'].values) data_arr = [] for i, item in enumerate(x): row = [QSurfaceDataItem(QVector3D(x[i], y[i], z[i]))] data_arr.append(row) print(len(data_arr)) self.surfaceproxy.resetArray(data_arr) def show_graph(self): self.surfaceseries.setDrawMode(QSurface3DSeries.DrawSurface) self.surfaceseries.setFlatShadingEnabled(True) self.graph.addSeries(self.surfaceseries) print(self.surfaceseries) if __name__ == "__main__": app = QApplication(sys.argv) g = Q3DSurface() container = QWidget.createWindowContainer(g) w = QWidget() layout = QHBoxLayout(w) layout.addWidget(container, 1) w.show() surf_graph = Surface(g) surf_graph.show_graph() sys.exit(app.exec())
Output:
-
Does the example you tried work? just to rule out a problem with the module and not your code.
I'm a bit concerned about object lives in your code, mainly this bit:data_arr = [] for i, item in enumerate(x): row = [QSurfaceDataItem(QVector3D(x[i], y[i], z[i]))] data_arr.append(row) print(len(data_arr)) self.surfaceproxy.resetArray(data_arr)
Are you certain those Items/Vectors are alive and accessible after that function is being called?
Can you verify that after the last line in your constructor? -
@CristianMaureira
Yes, the sample from the website does work perfectly. I copy pasted each of the modules, ran them locally, and produced exactly what that surface example shows.As far as what you asked, yes they do seem to be accessible. It also seems like they are being accessed as well, since the image of the plot shows the corresponding data in my data log on each axis.
My intention in that block of code was to populate a data item with a 3d vector containing the three x, y, z points for a specific index. then adding that into an array that gets passed into the proxy. That proxy gets passed into the series object, and ultimately that's where the drawing of the plot would happen. I tried to follow a similar flow to the example, which can be found under the surfacegraph.py file in the link of the sample.
It seems like my data makes it into the series, as the right numbers are being displayed on the plot itself. Just not sure why the actual figure never gets drawn.
-
Hi,
What happens if you make data_arr a member of your Surface class ?
Just a small side note, a Surface object that takes a "surface" parameter that is stored in a variable named graph makes your code harder to reason about.
-
@SGaist
Sorry about that, I tried to mimic the example but I can see how that is difficult to read.I am not 100% sure how to go about implementing that. According to the Qt Docs, the data types available would be QSurfaceDataProxy and QSurface3DSeries, I don't know what you are referring to exactly. I already turn that array into the data proxy object, which gets passed into series. Could you specify what you mean exactly? Not 100% sure I understand.
EDIT: I think I get what you meant. You were meaning for me to try doing something like 'self.data_arr = []' initialized in my constructor for the Surface class. This didn't yield any different results.
-
Have you tried casting to float QVector3D(float(x[]),... ), In https://codereview.qt-project.org/c/pyside/pyside-setup/+/406733 this was necessary.
-
@friedemannkleint Unfortunately that didn't do much, but thanks for sharing that resource maybe something in there can help me.