aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2022-04-29 14:21:07 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2022-05-05 16:04:43 +0200
commit75c7e1c7bc874a2ab6a76f32de8d4daa6d15b3ed (patch)
treebae4460377c0f9edcddd40279c3b39ddc6a0d346 /examples
parentde5f162a70dda9d74cfce2337a9f90ddcb4ca75f (diff)
QtDataVisualization: Add an example for using a numpy array directly
Task-number: PYSIDE-1880 Change-Id: I41a72f1ebcfa5f62826bffe6db670695b248bc7a Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'examples')
-rw-r--r--examples/datavisualization/surface_numpy/doc/surface_numpy.rst24
-rw-r--r--examples/datavisualization/surface_numpy/main.py226
-rw-r--r--examples/datavisualization/surface_numpy/surface_numpy.pyproject4
-rw-r--r--examples/datavisualization/surface_numpy/surfacegraph.py278
4 files changed, 532 insertions, 0 deletions
diff --git a/examples/datavisualization/surface_numpy/doc/surface_numpy.rst b/examples/datavisualization/surface_numpy/doc/surface_numpy.rst
new file mode 100644
index 000000000..ee003025d
--- /dev/null
+++ b/examples/datavisualization/surface_numpy/doc/surface_numpy.rst
@@ -0,0 +1,24 @@
+Surface Example
+===============
+
+Using Q3DSurface in a widget application.
+
+The surface example shows how to make a simple 3D surface graph using
+Q3DSurface and combining the use of widgets for adjusting several adjustable
+qualities. It requires building PySide6 with the ``--pyside-numpy-support``
+option. This example demonstrates the following features:
+
+* How to set up a QSurfaceDataProxy from a 2-dimensional numpy array.
+* How to use QHeightMapSurfaceDataProxy for showing 3D height maps.
+* Three different selection modes for studying the graph.
+* Axis range usage for displaying selected portions of the graph.
+* Changing theme.
+* How to set a custom surface gradient.
+
+For instructions about how to interact with the graph, see `this page`_.
+
+.. image:: ../../surface/doc/surface_mountain.png
+ :width: 400
+ :alt: Surface Example Screenshot
+
+.. _`this page`: https://doc.qt.io/qt-6/qtdatavisualization-interacting-with-data.html
diff --git a/examples/datavisualization/surface_numpy/main.py b/examples/datavisualization/surface_numpy/main.py
new file mode 100644
index 000000000..a46ed5d79
--- /dev/null
+++ b/examples/datavisualization/surface_numpy/main.py
@@ -0,0 +1,226 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+
+from PySide6.QtCore import QSize, Qt
+from PySide6.QtDataVisualization import Q3DSurface
+from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
+from PySide6.QtWidgets import (QApplication, QComboBox, QGroupBox, QHBoxLayout,
+ QLabel, QMessageBox, QPushButton, QRadioButton,
+ QSizePolicy, QSlider, QVBoxLayout, QWidget)
+
+from surfacegraph import SurfaceGraph
+
+THEMES = ["Qt", "Primary Colors", "Digia", "Stone Moss", "Army Blue", "Retro",
+ "Ebony", "Isabelle"]
+
+
+class Window(QWidget):
+ def __init__(self, graph, parent=None):
+ super().__init__(parent)
+ self._graph = graph
+ self._container = QWidget.createWindowContainer(self._graph, self,
+ Qt.Widget)
+
+ screen_size = self._graph.screen().size()
+ self._container.setMinimumSize(QSize(screen_size.width() / 2,
+ screen_size.height() / 1.6))
+ self._container.setMaximumSize(screen_size)
+ self._container.setSizePolicy(QSizePolicy.Expanding,
+ QSizePolicy.Expanding)
+ self._container.setFocusPolicy(Qt.StrongFocus)
+
+ h_layout = QHBoxLayout(self)
+ v_layout = QVBoxLayout()
+ h_layout.addWidget(self._container, 1)
+ h_layout.addLayout(v_layout)
+ v_layout.setAlignment(Qt.AlignTop)
+
+ model_group_box = QGroupBox("Model")
+
+ sqrt_sin_model_rb = QRadioButton(self)
+ sqrt_sin_model_rb.setText("Sqrt& Sin")
+ sqrt_sin_model_rb.setChecked(False)
+
+ height_map_model_rb = QRadioButton(self)
+ height_map_model_rb.setText("Height Map")
+ height_map_model_rb.setChecked(False)
+
+ model_vbox = QVBoxLayout()
+ model_vbox.addWidget(sqrt_sin_model_rb)
+ model_vbox.addWidget(height_map_model_rb)
+ model_group_box.setLayout(model_vbox)
+
+ selection_group_box = QGroupBox("Selection Mode")
+
+ mode_none_rb = QRadioButton(self)
+ mode_none_rb.setText("No selection")
+ mode_none_rb.setChecked(False)
+
+ mode_item_rb = QRadioButton(self)
+ mode_item_rb.setText("Item")
+ mode_item_rb.setChecked(False)
+
+ mode_slice_row_rb = QRadioButton(self)
+ mode_slice_row_rb.setText("Row Slice")
+ mode_slice_row_rb.setChecked(False)
+
+ mode_slice_column_rb = QRadioButton(self)
+ mode_slice_column_rb.setText("Column Slice")
+ mode_slice_column_rb.setChecked(False)
+
+ selection_vbox = QVBoxLayout()
+ selection_vbox.addWidget(mode_none_rb)
+ selection_vbox.addWidget(mode_item_rb)
+ selection_vbox.addWidget(mode_slice_row_rb)
+ selection_vbox.addWidget(mode_slice_column_rb)
+ selection_group_box.setLayout(selection_vbox)
+
+ axis_min_slider_x = QSlider(Qt.Horizontal, self)
+ axis_min_slider_x.setMinimum(0)
+ axis_min_slider_x.setTickInterval(1)
+ axis_min_slider_x.setEnabled(True)
+ axis_max_slider_x = QSlider(Qt.Horizontal, self)
+ axis_max_slider_x.setMinimum(1)
+ axis_max_slider_x.setTickInterval(1)
+ axis_max_slider_x.setEnabled(True)
+ axis_min_slider_z = QSlider(Qt.Horizontal, self)
+ axis_min_slider_z.setMinimum(0)
+ axis_min_slider_z.setTickInterval(1)
+ axis_min_slider_z.setEnabled(True)
+ axis_max_slider_z = QSlider(Qt.Horizontal, self)
+ axis_max_slider_z.setMinimum(1)
+ axis_max_slider_z.setTickInterval(1)
+ axis_max_slider_z.setEnabled(True)
+
+ theme_list = QComboBox(self)
+ theme_list.addItems(THEMES)
+
+ color_group_box = QGroupBox("Custom gradient")
+
+ gr_bto_y = QLinearGradient(0, 0, 1, 100)
+ gr_bto_y.setColorAt(1.0, Qt.black)
+ gr_bto_y.setColorAt(0.67, Qt.blue)
+ gr_bto_y.setColorAt(0.33, Qt.red)
+ gr_bto_y.setColorAt(0.0, Qt.yellow)
+
+ pm = QPixmap(24, 100)
+ pmp = QPainter(pm)
+ pmp.setBrush(QBrush(gr_bto_y))
+ pmp.setPen(Qt.NoPen)
+ pmp.drawRect(0, 0, 24, 100)
+ pmp.end()
+
+ gradient_bto_ypb = QPushButton(self)
+ gradient_bto_ypb.setIcon(QIcon(pm))
+ gradient_bto_ypb.setIconSize(QSize(24, 100))
+
+ gr_gto_r = QLinearGradient(0, 0, 1, 100)
+ gr_gto_r.setColorAt(1.0, Qt.darkGreen)
+ gr_gto_r.setColorAt(0.5, Qt.yellow)
+ gr_gto_r.setColorAt(0.2, Qt.red)
+ gr_gto_r.setColorAt(0.0, Qt.darkRed)
+ pmp.begin(pm)
+ pmp.setBrush(QBrush(gr_gto_r))
+ pmp.drawRect(0, 0, 24, 100)
+ pmp.end()
+
+ gradient_gto_rpb = QPushButton(self)
+ gradient_gto_rpb.setIcon(QIcon(pm))
+ gradient_gto_rpb.setIconSize(QSize(24, 100))
+
+ color_hbox = QHBoxLayout()
+ color_hbox.addWidget(gradient_bto_ypb)
+ color_hbox.addWidget(gradient_gto_rpb)
+ color_group_box.setLayout(color_hbox)
+
+ v_layout.addWidget(model_group_box)
+ v_layout.addWidget(selection_group_box)
+ v_layout.addWidget(QLabel("Column range"))
+ v_layout.addWidget(axis_min_slider_x)
+ v_layout.addWidget(axis_max_slider_x)
+ v_layout.addWidget(QLabel("Row range"))
+ v_layout.addWidget(axis_min_slider_z)
+ v_layout.addWidget(axis_max_slider_z)
+ v_layout.addWidget(QLabel("Theme"))
+ v_layout.addWidget(theme_list)
+ v_layout.addWidget(color_group_box)
+
+ self._modifier = SurfaceGraph(self._graph)
+
+ height_map_model_rb.toggled.connect(self._modifier.enable_height_map_model)
+ sqrt_sin_model_rb.toggled.connect(self._modifier.enable_sqrt_sin_model)
+ mode_none_rb.toggled.connect(self._modifier.toggle_mode_none)
+ mode_item_rb.toggled.connect(self._modifier.toggle_mode_item)
+ mode_slice_row_rb.toggled.connect(self._modifier.toggle_mode_slice_row)
+ mode_slice_column_rb.toggled.connect(self._modifier.toggle_mode_slice_column)
+ axis_min_slider_x.valueChanged.connect(self._modifier.adjust_xmin)
+ axis_max_slider_x.valueChanged.connect(self._modifier.adjust_xmax)
+ axis_min_slider_z.valueChanged.connect(self._modifier.adjust_zmin)
+ axis_max_slider_z.valueChanged.connect(self._modifier.adjust_zmax)
+ theme_list.currentIndexChanged[int].connect(self._modifier.change_theme)
+ gradient_bto_ypb.pressed.connect(self._modifier.set_black_to_yellow_gradient)
+ gradient_gto_rpb.pressed.connect(self._modifier.set_green_to_red_gradient)
+
+ self._modifier.set_axis_min_slider_x(axis_min_slider_x)
+ self._modifier.set_axis_max_slider_x(axis_max_slider_x)
+ self._modifier.set_axis_min_slider_z(axis_min_slider_z)
+ self._modifier.set_axis_max_slider_z(axis_max_slider_z)
+
+ sqrt_sin_model_rb.setChecked(True)
+ mode_item_rb.setChecked(True)
+ theme_list.setCurrentIndex(2)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ graph = Q3DSurface()
+ if not graph.hasContext():
+ msg_box = QMessageBox()
+ msg_box.setText("Couldn't initialize the OpenGL context.")
+ msg_box.exec()
+ sys.exit(-1)
+
+ window = Window(graph)
+ window.setWindowTitle("Surface example")
+ window.show()
+
+ sys.exit(app.exec())
diff --git a/examples/datavisualization/surface_numpy/surface_numpy.pyproject b/examples/datavisualization/surface_numpy/surface_numpy.pyproject
new file mode 100644
index 000000000..598a6541f
--- /dev/null
+++ b/examples/datavisualization/surface_numpy/surface_numpy.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py",
+ "surfacegraph.py"]
+}
diff --git a/examples/datavisualization/surface_numpy/surfacegraph.py b/examples/datavisualization/surface_numpy/surfacegraph.py
new file mode 100644
index 000000000..259877d10
--- /dev/null
+++ b/examples/datavisualization/surface_numpy/surfacegraph.py
@@ -0,0 +1,278 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import numpy as np
+import math
+from pathlib import Path
+
+from PySide6.QtCore import (QObject, Qt, Slot)
+from PySide6.QtDataVisualization import (Q3DTheme, QAbstract3DGraph,
+ QHeightMapSurfaceDataProxy,
+ QSurface3DSeries,
+ QSurfaceDataProxy,
+ QValue3DAxis)
+from PySide6.QtGui import QImage, QLinearGradient
+from PySide6.QtWidgets import QSlider
+
+SAMPLE_COUNT_X = 50
+SAMPLE_COUNT_Z = 50
+HEIGHT_MAP_GRID_STEP_X = 6
+HEIGHT_MAP_GRID_STEP_Z = 6
+SAMPLE_MIN = -8.0
+SAMPLE_MAX = 8.0
+
+
+def fill_proxy(p):
+ stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+ data = np.zeros((SAMPLE_COUNT_Z, SAMPLE_COUNT_X))
+ for i in range(SAMPLE_COUNT_Z):
+ # Keep values within range bounds, since just adding step can cause
+ # minor drift due to the rounding errors.
+ z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
+ for j in range(SAMPLE_COUNT_X):
+ x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
+ R = math.sqrt(z * z + x * x) + 0.01
+ y = (math.sin(R) / R + 0.24) * 1.61
+ data[i, j] = y
+ p.resetArrayNp(SAMPLE_MIN, stepX, SAMPLE_MIN, stepZ, data)
+
+
+class SurfaceGraph(QObject):
+ def __init__(self, surface, parent=None):
+ super().__init__(parent)
+
+ self.m_graph = surface
+ self.m_graph.setAxisX(QValue3DAxis())
+ self.m_graph.setAxisY(QValue3DAxis())
+ self.m_graph.setAxisZ(QValue3DAxis())
+
+ self.m_sqrtSinProxy = QSurfaceDataProxy()
+ fill_proxy(self.m_sqrtSinProxy)
+
+ self.m_sqrtSinSeries = QSurface3DSeries(self.m_sqrtSinProxy)
+
+ image_file = Path(__file__).parent.parent / "surface" / "mountain.png"
+ height_map_image = QImage(image_file)
+ self.m_heightMapProxy = QHeightMapSurfaceDataProxy(height_map_image)
+ self.m_heightMapSeries = QSurface3DSeries(self.m_heightMapProxy)
+ self.m_heightMapSeries.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self.m_heightMapProxy.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ self.m_heightMapWidth = height_map_image.width()
+ self.m_heightMapHeight = height_map_image.height()
+
+ self.m_axisMinSliderX = QSlider()
+ self.m_axisMaxSliderX = QSlider()
+ self.m_axisMinSliderZ = QSlider()
+ self.m_axisMaxSliderZ = QSlider()
+ self.m_rangeMinX = 0.0
+ self.m_rangeMinZ = 0.0
+ self.m_stepX = 0.0
+ self.m_stepZ = 0.0
+
+ def enable_sqrt_sin_model(self, enable):
+ if enable:
+ self.m_sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawSurfaceAndWireframe)
+ self.m_sqrtSinSeries.setFlatShadingEnabled(True)
+
+ self.m_graph.axisX().setLabelFormat("%.2f")
+ self.m_graph.axisZ().setLabelFormat("%.2f")
+ self.m_graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self.m_graph.axisY().setRange(0.0, 2.0)
+ self.m_graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self.m_graph.axisX().setLabelAutoRotation(30)
+ self.m_graph.axisY().setLabelAutoRotation(90)
+ self.m_graph.axisZ().setLabelAutoRotation(30)
+
+ self.m_graph.removeSeries(self.m_heightMapSeries)
+ self.m_graph.addSeries(self.m_sqrtSinSeries)
+
+ # Reset range sliders for Sqrt&Sin
+ self.m_rangeMinX = SAMPLE_MIN
+ self.m_rangeMinZ = SAMPLE_MIN
+ self.m_stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ self.m_stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+ self.m_axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
+ self.m_axisMinSliderX.setValue(0)
+ self.m_axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
+ self.m_axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
+ self.m_axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
+ self.m_axisMinSliderZ.setValue(0)
+ self.m_axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
+ self.m_axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
+
+ def enable_height_map_model(self, enable):
+ if enable:
+ self.m_heightMapSeries.setDrawMode(QSurface3DSeries.DrawSurface)
+ self.m_heightMapSeries.setFlatShadingEnabled(False)
+
+ self.m_graph.axisX().setLabelFormat("%.1f N")
+ self.m_graph.axisZ().setLabelFormat("%.1f E")
+ self.m_graph.axisX().setRange(34.0, 40.0)
+ self.m_graph.axisY().setAutoAdjustRange(True)
+ self.m_graph.axisZ().setRange(18.0, 24.0)
+
+ self.m_graph.axisX().setTitle("Latitude")
+ self.m_graph.axisY().setTitle("Height")
+ self.m_graph.axisZ().setTitle("Longitude")
+
+ self.m_graph.removeSeries(self.m_sqrtSinSeries)
+ self.m_graph.addSeries(self.m_heightMapSeries)
+
+ # Reset range sliders for height map
+ map_grid_count_x = self.m_heightMapWidth / HEIGHT_MAP_GRID_STEP_X
+ map_grid_count_z = self.m_heightMapHeight / HEIGHT_MAP_GRID_STEP_Z
+ self.m_rangeMinX = 34.0
+ self.m_rangeMinZ = 18.0
+ self.m_stepX = 6.0 / float(map_grid_count_x - 1)
+ self.m_stepZ = 6.0 / float(map_grid_count_z - 1)
+ self.m_axisMinSliderX.setMaximum(map_grid_count_x - 2)
+ self.m_axisMinSliderX.setValue(0)
+ self.m_axisMaxSliderX.setMaximum(map_grid_count_x - 1)
+ self.m_axisMaxSliderX.setValue(map_grid_count_x - 1)
+ self.m_axisMinSliderZ.setMaximum(map_grid_count_z - 2)
+ self.m_axisMinSliderZ.setValue(0)
+ self.m_axisMaxSliderZ.setMaximum(map_grid_count_z - 1)
+ self.m_axisMaxSliderZ.setValue(map_grid_count_z - 1)
+
+ def adjust_xmin(self, minimum):
+ min_x = self.m_stepX * float(minimum) + self.m_rangeMinX
+
+ maximum = self.m_axisMaxSliderX.value()
+ if minimum >= maximum:
+ maximum = minimum + 1
+ self.m_axisMaxSliderX.setValue(maximum)
+ max_x = self.m_stepX * maximum + self.m_rangeMinX
+
+ self.set_axis_xrange(min_x, max_x)
+
+ def adjust_xmax(self, maximum):
+ max_x = self.m_stepX * float(maximum) + self.m_rangeMinX
+
+ minimum = self.m_axisMinSliderX.value()
+ if maximum <= minimum:
+ minimum = maximum - 1
+ self.m_axisMinSliderX.setValue(minimum)
+ min_x = self.m_stepX * minimum + self.m_rangeMinX
+
+ self.set_axis_xrange(min_x, max_x)
+
+ def adjust_zmin(self, minimum):
+ min_z = self.m_stepZ * float(minimum) + self.m_rangeMinZ
+
+ maximum = self.m_axisMaxSliderZ.value()
+ if minimum >= maximum:
+ maximum = minimum + 1
+ self.m_axisMaxSliderZ.setValue(maximum)
+ max_z = self.m_stepZ * maximum + self.m_rangeMinZ
+
+ self.set_axis_zrange(min_z, max_z)
+
+ def adjust_zmax(self, maximum):
+ max_x = self.m_stepZ * float(maximum) + self.m_rangeMinZ
+
+ minimum = self.m_axisMinSliderZ.value()
+ if maximum <= minimum:
+ minimum = maximum - 1
+ self.m_axisMinSliderZ.setValue(minimum)
+ min_x = self.m_stepZ * minimum + self.m_rangeMinZ
+
+ self.set_axis_zrange(min_x, max_x)
+
+ def set_axis_xrange(self, minimum, maximum):
+ self.m_graph.axisX().setRange(minimum, maximum)
+
+ def set_axis_zrange(self, minimum, maximum):
+ self.m_graph.axisZ().setRange(minimum, maximum)
+
+ @Slot()
+ def change_theme(self, theme):
+ self.m_graph.activeTheme().setType(Q3DTheme.Theme(theme))
+
+ def set_black_to_yellow_gradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.black)
+ gr.setColorAt(0.33, Qt.blue)
+ gr.setColorAt(0.67, Qt.red)
+ gr.setColorAt(1.0, Qt.yellow)
+
+ series = self.m_graph.seriesList()[0]
+ series.setBaseGradient(gr)
+ series.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ def set_green_to_red_gradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.darkGreen)
+ gr.setColorAt(0.5, Qt.yellow)
+ gr.setColorAt(0.8, Qt.red)
+ gr.setColorAt(1.0, Qt.darkRed)
+
+ series = self.m_graph.seriesList()[0]
+ series.setBaseGradient(gr)
+ series.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ def toggle_mode_none(self):
+ self.m_graph.setSelectionMode(QAbstract3DGraph.SelectionNone)
+
+ def toggle_mode_item(self):
+ self.m_graph.setSelectionMode(QAbstract3DGraph.SelectionItem)
+
+ def toggle_mode_slice_row(self):
+ self.m_graph.setSelectionMode(
+ QAbstract3DGraph.SelectionItemAndRow | QAbstract3DGraph.SelectionSlice
+ )
+
+ def toggle_mode_slice_column(self):
+ self.m_graph.setSelectionMode(
+ QAbstract3DGraph.SelectionItemAndColumn | QAbstract3DGraph.SelectionSlice
+ )
+
+ def set_axis_min_slider_x(self, slider):
+ self.m_axisMinSliderX = slider
+
+ def set_axis_max_slider_x(self, slider):
+ self.m_axisMaxSliderX = slider
+
+ def set_axis_min_slider_z(self, slider):
+ self.m_axisMinSliderZ = slider
+
+ def set_axis_max_slider_z(self, slider):
+ self.m_axisMaxSliderZ = slider