mirror of https://github.com/qt/qtgraphs.git
Create offscreen slice view of given index and grab it to image
Slice view creation of given index of row or column The requested slice view is rendered outisde of window and grabbed to QQuickGrabbedResult. QML side provides to save it to a specified path. grabsliceview manual test for feature test Task-number: QTBUG-105607 Change-Id: Ia8019f99134abbb328a027b656ba85552ffcaf92 Reviewed-by: Kwanghyo Park <kwanghyo.park@qt.io>
This commit is contained in:
parent
426fa3fc01
commit
609e036600
|
|
@ -16,6 +16,7 @@
|
|||
#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
|
||||
#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
|
||||
#include <QtQuick3D/private/qquick3drepeater_p.h>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
|
||||
/*!
|
||||
* \qmltype Bars3D
|
||||
|
|
@ -176,6 +177,16 @@
|
|||
* \sa GraphsItem3D::hasSeries()
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod void Bars3D::renderSliceToImage(int requestedIndex, QtGraphs3D::SliceType sliceType, QUrl filePath)
|
||||
* \since 6.10
|
||||
*
|
||||
* Exports a 2d slice from series at \a requestedIndex and saves the result to an image
|
||||
* at a specified \a filePath.
|
||||
* The exported slice includes bars of row or column, which is defined by
|
||||
* \a sliceType.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlsignal Bars3D::multiSeriesUniformChanged(bool uniform)
|
||||
*
|
||||
|
|
@ -705,6 +716,200 @@ float QQuickGraphsBars::floorLevel() const
|
|||
return m_floorLevel;
|
||||
}
|
||||
|
||||
QQuick3DViewport *QQuickGraphsBars::createOffscreenSliceView(int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
QQuick3DViewport *sliceView = QQuickGraphsItem::createOffscreenSliceView(sliceType);
|
||||
|
||||
bool isRow = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceRow);
|
||||
bool isColumn = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceColumn);
|
||||
|
||||
QList<QBar3DSeries *> barSeriesList = this->barSeriesList();
|
||||
for (const auto &barSeries : std::as_const(barSeriesList)) {
|
||||
qsizetype newRowSize = qMin(barSeries->dataProxy()->rowCount() - m_minRow, m_newRows);
|
||||
qsizetype newColSize = 0;
|
||||
if (newRowSize) {
|
||||
const QBarDataRow *dataRow = &barSeries->dataProxy()->rowAt(m_minRow);
|
||||
if (dataRow) {
|
||||
qsizetype dataColIndex = m_minCol;
|
||||
newColSize = qMin(dataRow->size() - dataColIndex, m_newCols);
|
||||
}
|
||||
}
|
||||
|
||||
if (!barSeries->isVisible())
|
||||
continue;
|
||||
|
||||
if (requestedIndex < 0 || requestedIndex >= newRowSize || requestedIndex >= newColSize) {
|
||||
qWarning("The index is out of range. The render stops.");
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
qsizetype slicedBarListSize = -1;
|
||||
|
||||
if (isRow)
|
||||
slicedBarListSize = newColSize;
|
||||
else if (isColumn)
|
||||
slicedBarListSize = newRowSize;
|
||||
|
||||
if (slicedBarListSize < 0)
|
||||
return nullptr;
|
||||
|
||||
QList<BarModel *> barList = *m_barModelsMap.value(barSeries);
|
||||
bool useGradient = barSeries->d_func()->isUsingGradient();
|
||||
bool rangeGradient =
|
||||
(useGradient
|
||||
&& barSeries->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
|
||||
|
||||
QQuick3DModel *model = nullptr;
|
||||
QList<BarItemHolder *> barItemHolderList;
|
||||
QList<BarItemHolder *> barItemList;
|
||||
QList<BarItemHolder *> selectedItems;
|
||||
if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
|
||||
model = new QQuick3DModel();
|
||||
model->setParent(sliceView->scene());
|
||||
model->setParentItem(sliceView->scene());
|
||||
model->setObjectName(QStringLiteral("BarModel"));
|
||||
QString fileName = getMeshFileName();
|
||||
if (fileName.isEmpty())
|
||||
fileName = barSeries->userDefinedMesh();
|
||||
|
||||
model->setSource(QUrl(fileName));
|
||||
|
||||
auto barInstancing = new BarInstancing;
|
||||
barInstancing->setParent(barSeries);
|
||||
model->setInstancing(barInstancing);
|
||||
|
||||
BarModel *barListItem = barList.at(0);
|
||||
updateItemMaterial(model, useGradient, rangeGradient,
|
||||
QStringLiteral(":/materials/BarsMaterialInstancing"));
|
||||
updateMaterialProperties(model, false, false, barListItem->texture,
|
||||
barSeries->baseColor());
|
||||
|
||||
barItemList = barListItem->instancing->dataArray();
|
||||
for (const auto bih : std::as_const(barItemList)) {
|
||||
if (!((isRow && bih->coord.x() == requestedIndex)
|
||||
|| (isColumn && bih->coord.y() == requestedIndex)))
|
||||
continue;
|
||||
|
||||
BarItemHolder *selectedBih = new BarItemHolder();
|
||||
selectedBih->selectedBar = false;
|
||||
selectedBih->coord = bih->coord;
|
||||
selectedBih->rotation = bih->rotation;
|
||||
selectedBih->heightValue = bih->heightValue;
|
||||
selectedBih->position = bih->position;
|
||||
selectedBih->scale = bih->scale;
|
||||
|
||||
selectedItems.push_back(selectedBih);
|
||||
}
|
||||
if (selectedItems.size() == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
qsizetype index = 0;
|
||||
for (int ind = 0; ind < slicedBarListSize; ++ind) {
|
||||
if (isRow)
|
||||
index = (requestedIndex * barSeries->dataProxy()->colCount()) + ind;
|
||||
else
|
||||
index = requestedIndex + (ind * barSeries->dataProxy()->colCount());
|
||||
|
||||
if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
|
||||
if (index > barList.size())
|
||||
return nullptr;
|
||||
model = createDataItem(sliceView->scene(), barSeries);
|
||||
BarModel *barModel = barList.at(index);
|
||||
if (isRow) {
|
||||
model->setPosition(QVector3D(barModel->model->x(), barModel->model->y(), 0.0f));
|
||||
} else {
|
||||
model->setX(barModel->model->z() - (barList.at(0)->visualIndex * .1f));
|
||||
model->setY(barModel->model->y());
|
||||
model->setZ(0.0f);
|
||||
}
|
||||
model->setScale(barModel->model->scale());
|
||||
|
||||
updateItemMaterial(model, useGradient, rangeGradient,
|
||||
QStringLiteral(":/materials/BarsMaterial"));
|
||||
|
||||
updateMaterialProperties(model, false, false, barList.at(index)->texture,
|
||||
barSeries->baseColor());
|
||||
} else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
|
||||
BarModel *barModel = barList.at(0);
|
||||
BarItemHolder *itemHolder = new BarItemHolder();
|
||||
itemHolder->selectedBar = false;
|
||||
itemHolder->color = barSeries->baseColor();
|
||||
itemHolder->coord = barModel->coord;
|
||||
itemHolder->rotation = selectedItems.at(ind)->rotation;
|
||||
itemHolder->heightValue = barModel->heightValue;
|
||||
itemHolder->position = selectedItems.at(ind)->position;
|
||||
itemHolder->scale = selectedItems.at(ind)->scale;
|
||||
|
||||
if (isRow) {
|
||||
itemHolder->position.setZ(.0f);
|
||||
} else {
|
||||
itemHolder->position.setX(itemHolder->position.z()
|
||||
- (barModel->visualIndex * .1f));
|
||||
itemHolder->position.setZ(.0f);
|
||||
}
|
||||
|
||||
barItemHolderList.push_back(itemHolder);
|
||||
}
|
||||
}
|
||||
|
||||
if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
|
||||
BarInstancing *instancing = static_cast<BarInstancing *>(model->instancing());
|
||||
instancing->setDataArray(barItemHolderList);
|
||||
}
|
||||
}
|
||||
|
||||
return sliceView;
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> QQuickGraphsBars::renderSliceToImage(
|
||||
int requestedIndex, QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
QQuick3DViewport *sliceView = createOffscreenSliceView(requestedIndex, sliceType);
|
||||
|
||||
if (!sliceView)
|
||||
return QSharedPointer<QQuickItemGrabResult>();
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
|
||||
connect(grabbed.data(), &QQuickItemGrabResult::ready, this, [sliceView]() {
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
});
|
||||
|
||||
return grabbed;
|
||||
}
|
||||
|
||||
void QQuickGraphsBars::renderSliceToImage(int requestedIndex, QtGraphs3D::SliceType sliceType,
|
||||
const QUrl &filePath)
|
||||
{
|
||||
QQuick3DViewport *sliceView = createOffscreenSliceView(requestedIndex, sliceType);
|
||||
|
||||
if (!sliceView)
|
||||
return;
|
||||
|
||||
if (filePath.isEmpty()) {
|
||||
qWarning("Save path is not defined.");
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
|
||||
connect(grabbed.data(), &QQuickItemGrabResult::ready, this, [grabbed, sliceView, filePath]() {
|
||||
if (!grabbed.data()->saveToFile(filePath))
|
||||
qWarning("Saving requested slice view to image failed");
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void QQuickGraphsBars::componentComplete()
|
||||
{
|
||||
QQuickGraphsItem::componentComplete();
|
||||
|
|
@ -2238,7 +2443,7 @@ void QQuickGraphsBars::updateSelectedBar()
|
|||
}
|
||||
}
|
||||
|
||||
QQuickGraphsItem::SelectionType QQuickGraphsBars::isSelected(int row, int bar, QBar3DSeries *series)
|
||||
QQuickGraphsBars::SelectionType QQuickGraphsBars::isSelected(int row, int bar, QBar3DSeries *series)
|
||||
{
|
||||
QQuickGraphsBars::SelectionType isSelectedType = QQuickGraphsBars::SelectionNone;
|
||||
if ((selectionMode().testFlag(QtGraphs3D::SelectionFlag::MultiSeries) && m_selectedBarSeries)
|
||||
|
|
@ -2313,6 +2518,7 @@ void QQuickGraphsBars::createSliceView()
|
|||
{
|
||||
setSliceOrthoProjection(false);
|
||||
QQuickGraphsItem::createSliceView();
|
||||
|
||||
QList<QBar3DSeries *> barSeries = barSeriesList();
|
||||
for (const auto &barSeries : std::as_const(barSeries)) {
|
||||
QList<BarModel *> &slicedBarList = m_slicedBarModels[barSeries];
|
||||
|
|
|
|||
|
|
@ -149,6 +149,12 @@ public:
|
|||
bool isDataDirty() const { return m_isDataDirty; }
|
||||
void setDataDirty(bool dirty) { m_isDataDirty = dirty; }
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> renderSliceToImage(int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType);
|
||||
Q_REVISION(6, 10)
|
||||
Q_INVOKABLE void renderSliceToImage(int requestedIndex, QtGraphs3D::SliceType sliceType,
|
||||
const QUrl &filePath);
|
||||
|
||||
protected:
|
||||
void componentComplete() override;
|
||||
void synchData() override;
|
||||
|
|
@ -172,6 +178,9 @@ protected:
|
|||
QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation) override;
|
||||
void updateSliceItemLabel(const QString &label, QVector3D position) override;
|
||||
|
||||
QQuick3DViewport* createOffscreenSliceView(int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType);
|
||||
|
||||
public Q_SLOTS:
|
||||
void handleAxisXChanged(QAbstract3DAxis *axis) override;
|
||||
void handleAxisYChanged(QAbstract3DAxis *axis) override;
|
||||
|
|
@ -205,6 +214,13 @@ Q_SIGNALS:
|
|||
void floorLevelChanged(float level);
|
||||
|
||||
private:
|
||||
enum SelectionType {
|
||||
SelectionNone = 0,
|
||||
SelectionItem,
|
||||
SelectionRow,
|
||||
SelectionColumn,
|
||||
};
|
||||
|
||||
Bars3DChangeBitField m_changeTracker;
|
||||
QList<ChangeItem> m_changedItems;
|
||||
QList<ChangeRow> m_changedRows;
|
||||
|
|
@ -320,7 +336,7 @@ private:
|
|||
void deleteBarItemHolders(BarInstancing *instancing);
|
||||
QQuick3DTexture *createTexture();
|
||||
void updateSelectedBar();
|
||||
QQuickGraphsItem::SelectionType isSelected(int row, int bar, QBar3DSeries *series);
|
||||
SelectionType isSelected(int row, int bar, QBar3DSeries *series);
|
||||
void resetClickedStatus();
|
||||
void removeSlicedBarModels();
|
||||
void createBarItemHolders(QBar3DSeries *series, QList<BarModel *> barList, bool slice);
|
||||
|
|
|
|||
|
|
@ -2876,7 +2876,7 @@ void QQuickGraphsItem::synchData()
|
|||
changeLabelTextColor(m_repeaterX, labelTextColor);
|
||||
m_titleLabelX->setProperty("labelTextColor", labelTextColor);
|
||||
if (m_sliceView && isSliceEnabled()) {
|
||||
if (m_selectionMode == SelectionRow)
|
||||
if (m_selectionMode == QtGraphs3D::SelectionFlag::Row)
|
||||
changeLabelTextColor(m_sliceHorizontalLabelRepeater, labelTextColor);
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelTextColor", labelTextColor);
|
||||
}
|
||||
|
|
@ -2899,7 +2899,7 @@ void QQuickGraphsItem::synchData()
|
|||
changeLabelTextColor(m_repeaterZ, labelTextColor);
|
||||
m_titleLabelZ->setProperty("labelTextColor", labelTextColor);
|
||||
if (m_sliceView && isSliceEnabled()) {
|
||||
if (m_selectionMode == SelectionColumn)
|
||||
if (m_selectionMode == QtGraphs3D::SelectionFlag::Column)
|
||||
changeLabelTextColor(m_sliceHorizontalLabelRepeater, labelTextColor);
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelTextColor", labelTextColor);
|
||||
}
|
||||
|
|
@ -5027,9 +5027,9 @@ void QQuickGraphsItem::handleLabelCountChanged(QQuick3DRepeater *repeater, QColo
|
|||
theme()->isLabelBackgroundVisible());
|
||||
changeLabelBorderVisible(m_sliceHorizontalLabelRepeater, theme()->isLabelBorderVisible());
|
||||
changeLabelBorderVisible(m_sliceVerticalLabelRepeater, theme()->isLabelBorderVisible());
|
||||
if (m_selectionMode == SelectionRow)
|
||||
if (m_selectionMode == QtGraphs3D::SelectionFlag::Row)
|
||||
changeLabelTextColor(m_sliceHorizontalLabelRepeater, theme()->axisX().labelTextColor());
|
||||
else if (m_selectionMode == SelectionColumn)
|
||||
else if (m_selectionMode == QtGraphs3D::SelectionFlag::Column)
|
||||
changeLabelTextColor(m_sliceHorizontalLabelRepeater, theme()->axisZ().labelTextColor());
|
||||
changeLabelTextColor(m_sliceVerticalLabelRepeater, theme()->axisY().labelTextColor());
|
||||
changeLabelFont(m_sliceHorizontalLabelRepeater, theme()->labelFont());
|
||||
|
|
@ -6352,7 +6352,7 @@ void QQuickGraphsItem::createSliceView()
|
|||
|
||||
auto scene = m_sliceView->scene();
|
||||
|
||||
createSliceCamera();
|
||||
createSliceCamera(m_sliceView);
|
||||
|
||||
// auto gridDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
|
||||
m_labelDelegate.reset(new QQmlComponent(qmlEngine(this), QStringLiteral(":/axis/AxisLabel")));
|
||||
|
|
@ -6390,40 +6390,102 @@ void QQuickGraphsItem::createSliceView()
|
|||
m_sliceItemLabel->setVisible(false);
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::createSliceCamera()
|
||||
QQuick3DViewport *QQuickGraphsItem::createOffscreenSliceView(QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
auto sliceView = new QQuick3DViewport();
|
||||
sliceView->setParent(this);
|
||||
sliceView->setParentItem(this);
|
||||
sliceView->setWidth(parentItem()->width() * .5);
|
||||
sliceView->setHeight(parentItem()->height() * .5);
|
||||
sliceView->setX(sliceView->width() * -1);
|
||||
sliceView->environment()->setBackgroundMode(
|
||||
QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes::Color);
|
||||
sliceView->environment()->setClearColor(environment()->clearColor());
|
||||
sliceView->setRenderMode(renderMode());
|
||||
|
||||
auto scene = sliceView->scene();
|
||||
|
||||
createSliceCamera(sliceView);
|
||||
|
||||
std::unique_ptr<QQmlComponent> labelDelegate;
|
||||
labelDelegate.reset(new QQmlComponent(qmlEngine(this), QStringLiteral(":/axis/AxisLabel")));
|
||||
|
||||
auto sliceGridGeometryModel = new QQuick3DModel(scene);
|
||||
|
||||
auto sliceGridGeometry = new QQuick3DGeometry(sliceGridGeometryModel);
|
||||
sliceGridGeometry->setStride(sizeof(QVector3D));
|
||||
sliceGridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
|
||||
sliceGridGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
||||
0,
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
sliceGridGeometryModel->setGeometry(sliceGridGeometry);
|
||||
|
||||
QQmlListReference gridMaterialRef(sliceGridGeometryModel, "materials");
|
||||
auto gridMaterial = new QQuick3DPrincipledMaterial(sliceGridGeometryModel);
|
||||
gridMaterial->setLighting(QQuick3DPrincipledMaterial::Lighting::NoLighting);
|
||||
gridMaterial->setCullMode(QQuick3DMaterial::CullMode::BackFaceCulling);
|
||||
gridMaterial->setBaseColor(Qt::red);
|
||||
gridMaterialRef.append(gridMaterial);
|
||||
|
||||
updateSliceGrid(sliceGridGeometryModel, sliceType);
|
||||
|
||||
auto sliceHorizontalLabelRepeater = createRepeater(scene);
|
||||
sliceHorizontalLabelRepeater->setDelegate(labelDelegate.get());
|
||||
|
||||
auto sliceVerticalLabelRepeater = createRepeater(scene);
|
||||
sliceVerticalLabelRepeater->setDelegate(labelDelegate.get());
|
||||
|
||||
auto sliceHorizontalTitleLabel = createTitleLabel(scene);
|
||||
sliceHorizontalTitleLabel->setVisible(true);
|
||||
|
||||
auto sliceVerticalTitleLabel = createTitleLabel(scene);
|
||||
sliceVerticalTitleLabel->setVisible(true);
|
||||
|
||||
auto sliceItemLabel = createTitleLabel(scene);
|
||||
sliceItemLabel->setVisible(false);
|
||||
|
||||
updateSliceLabels(sliceHorizontalLabelRepeater, sliceVerticalLabelRepeater,
|
||||
sliceHorizontalTitleLabel, sliceVerticalTitleLabel, sliceItemLabel,
|
||||
sliceType);
|
||||
|
||||
return sliceView;
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::createSliceCamera(QQuick3DViewport *sliceView)
|
||||
{
|
||||
if (isSliceOrthoProjection()) {
|
||||
auto camera = new QQuick3DOrthographicCamera(sliceView()->scene());
|
||||
auto camera = new QQuick3DOrthographicCamera(sliceView->scene());
|
||||
camera->setPosition(QVector3D(.0f, .0f, 20.0f));
|
||||
const float scale = qMin(sliceView()->width(), sliceView()->height());
|
||||
const float scale = qMin(sliceView->width(), sliceView->height());
|
||||
const float magnificationScaleFactor = 2 * window()->devicePixelRatio()
|
||||
* .08f; // this controls the size of the slice view
|
||||
const float magnification = scale * magnificationScaleFactor;
|
||||
camera->setHorizontalMagnification(magnification);
|
||||
camera->setVerticalMagnification(magnification);
|
||||
sliceView()->setCamera(camera);
|
||||
sliceView->setCamera(camera);
|
||||
|
||||
auto light = new QQuick3DDirectionalLight(sliceView()->scene());
|
||||
auto light = new QQuick3DDirectionalLight(sliceView->scene());
|
||||
light->setParent(camera);
|
||||
light->setParentItem(camera);
|
||||
} else {
|
||||
auto camera = new QQuick3DPerspectiveCamera(sliceView()->scene());
|
||||
auto camera = new QQuick3DPerspectiveCamera(sliceView->scene());
|
||||
camera->setFieldOfViewOrientation(
|
||||
QQuick3DPerspectiveCamera::FieldOfViewOrientation::Vertical);
|
||||
camera->setClipNear(5.f);
|
||||
camera->setClipFar(15.f);
|
||||
camera->setFieldOfView(35.f);
|
||||
camera->setPosition(QVector3D(.0f, .0f, 10.f));
|
||||
sliceView()->setCamera(camera);
|
||||
sliceView->setCamera(camera);
|
||||
|
||||
auto light = new QQuick3DDirectionalLight(sliceView()->scene());
|
||||
auto light = new QQuick3DDirectionalLight(sliceView->scene());
|
||||
light->setParent(camera);
|
||||
light->setParentItem(camera);
|
||||
light->setAmbientColor(QColor::fromRgbF(1.f, 1.f, 1.f));
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::updateSliceGrid()
|
||||
void QQuickGraphsItem::updateSliceGrid(QQuick3DModel *sliceGridGeometryModel,
|
||||
QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
QAbstract3DAxis *horizontalAxis = nullptr;
|
||||
QAbstract3DAxis *verticalAxis = axisY();
|
||||
|
|
@ -6433,12 +6495,17 @@ void QQuickGraphsItem::updateSliceGrid()
|
|||
|
||||
float horizontalScale = 0.0f;
|
||||
|
||||
if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)) {
|
||||
bool isRow = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceRow);
|
||||
bool isColumn = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceColumn);
|
||||
|
||||
if (isRow) {
|
||||
horizontalAxis = axisX();
|
||||
horizontalScale = backgroundScale.x();
|
||||
scale = m_scaleWithBackground.x();
|
||||
translate = m_scaleWithBackground.x();
|
||||
} else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)) {
|
||||
} else if (isColumn) {
|
||||
horizontalAxis = axisZ();
|
||||
horizontalScale = backgroundScale.z();
|
||||
scale = m_scaleWithBackground.z();
|
||||
|
|
@ -6516,16 +6583,26 @@ void QQuickGraphsItem::updateSliceGrid()
|
|||
}
|
||||
}
|
||||
|
||||
auto geometry = m_sliceGridGeometryModel->geometry();
|
||||
QQuick3DModel *sliceModel = nullptr;
|
||||
if (sliceGridGeometryModel)
|
||||
sliceModel = sliceGridGeometryModel;
|
||||
else
|
||||
sliceModel = m_sliceGridGeometryModel;
|
||||
QQuick3DGeometry *geometry = sliceModel->geometry();
|
||||
geometry->setVertexData(vertices);
|
||||
geometry->update();
|
||||
|
||||
QQmlListReference materialRef(m_sliceGridGeometryModel, "materials");
|
||||
QQmlListReference materialRef(sliceModel, "materials");
|
||||
auto material = static_cast<QQuick3DPrincipledMaterial *>(materialRef.at(0));
|
||||
material->setBaseColor(theme()->grid().mainColor());
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::updateSliceLabels()
|
||||
void QQuickGraphsItem::updateSliceLabels(QQuick3DRepeater *horizontalLabel,
|
||||
QQuick3DRepeater *verticalLabel,
|
||||
QQuick3DNode *horizontalTitle,
|
||||
QQuick3DNode *verticalTitle,
|
||||
QQuick3DNode *itemLabel,
|
||||
QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
QAbstract3DAxis *horizontalAxis = nullptr;
|
||||
QAbstract3DAxis *verticalAxis = axisY();
|
||||
|
|
@ -6534,12 +6611,29 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
float translate;
|
||||
QColor horizontalLabelTextColor;
|
||||
|
||||
if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)) {
|
||||
QQuick3DRepeater *sliceHorizontalLabelRepeater = nullptr;
|
||||
if (horizontalLabel)
|
||||
sliceHorizontalLabelRepeater = horizontalLabel;
|
||||
else
|
||||
sliceHorizontalLabelRepeater = m_sliceHorizontalLabelRepeater;
|
||||
|
||||
QQuick3DRepeater *sliceVerticalLabelRepeater = nullptr;
|
||||
if (verticalLabel)
|
||||
sliceVerticalLabelRepeater = verticalLabel;
|
||||
else
|
||||
sliceVerticalLabelRepeater = m_sliceVerticalLabelRepeater;
|
||||
|
||||
bool isRow = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceRow);
|
||||
bool isColumn = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceColumn);
|
||||
|
||||
if (isRow) {
|
||||
horizontalAxis = axisX();
|
||||
scale = backgroundScale.x() - m_backgroundScaleMargin.x();
|
||||
translate = backgroundScale.x() - m_backgroundScaleMargin.x();
|
||||
horizontalLabelTextColor = theme()->axisX().labelTextColor();
|
||||
} else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)) {
|
||||
} else if (isColumn) {
|
||||
horizontalAxis = axisZ();
|
||||
scale = backgroundScale.z() - m_backgroundScaleMargin.z();
|
||||
translate = backgroundScale.z() - m_backgroundScaleMargin.z();
|
||||
|
|
@ -6553,20 +6647,20 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
|
||||
if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Value) {
|
||||
QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
|
||||
m_sliceHorizontalLabelRepeater->model().clear();
|
||||
m_sliceHorizontalLabelRepeater->setModel(valueAxis->labels().size());
|
||||
sliceHorizontalLabelRepeater->model().clear();
|
||||
sliceHorizontalLabelRepeater->setModel(valueAxis->labels().size());
|
||||
} else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) {
|
||||
m_sliceHorizontalLabelRepeater->model().clear();
|
||||
m_sliceHorizontalLabelRepeater->setModel(horizontalAxis->labels().size());
|
||||
sliceHorizontalLabelRepeater->model().clear();
|
||||
sliceHorizontalLabelRepeater->setModel(horizontalAxis->labels().size());
|
||||
}
|
||||
|
||||
if (verticalAxis->type() == QAbstract3DAxis::AxisType::Value) {
|
||||
QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
|
||||
m_sliceVerticalLabelRepeater->model().clear();
|
||||
m_sliceVerticalLabelRepeater->setModel(valueAxis->labels().size());
|
||||
sliceVerticalLabelRepeater->model().clear();
|
||||
sliceVerticalLabelRepeater->setModel(valueAxis->labels().size());
|
||||
} else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) {
|
||||
m_sliceVerticalLabelRepeater->model().clear();
|
||||
m_sliceVerticalLabelRepeater->setModel(verticalAxis->labels().size());
|
||||
sliceVerticalLabelRepeater->model().clear();
|
||||
sliceVerticalLabelRepeater->setModel(verticalAxis->labels().size());
|
||||
}
|
||||
|
||||
float textPadding = 12.0f;
|
||||
|
|
@ -6591,8 +6685,8 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
QColor backgroundColor = theme()->labelBackgroundColor();
|
||||
|
||||
if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Value) {
|
||||
for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) {
|
||||
auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(i));
|
||||
for (int i = 0; i < sliceHorizontalLabelRepeater->count(); i++) {
|
||||
auto obj = static_cast<QQuick3DNode *>(sliceHorizontalLabelRepeater->objectAt(i));
|
||||
// It is important to use the position of vertical grids so that they can be in the same
|
||||
// position when col/row ranges are updated.
|
||||
float linePosX = static_cast<QValue3DAxis *>(horizontalAxis)->gridPositionAt(i) * scale
|
||||
|
|
@ -6615,13 +6709,13 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
obj->setVisible(false);
|
||||
}
|
||||
} else if (horizontalAxis->type() == QAbstract3DAxis::AxisType::Category) {
|
||||
for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) {
|
||||
for (int i = 0; i < sliceHorizontalLabelRepeater->count(); i++) {
|
||||
labelTrans = calculateCategoryLabelPosition(horizontalAxis, labelTrans, i);
|
||||
labelTrans.setY(-yPos /*- (adjustment / 2.f)*/);
|
||||
if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column))
|
||||
if (isColumn)
|
||||
labelTrans.setX(labelTrans.z());
|
||||
labelTrans.setZ(1.0f); // Bring the labels on top of bars and grid
|
||||
auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(i));
|
||||
auto obj = static_cast<QQuick3DNode *>(sliceHorizontalLabelRepeater->objectAt(i));
|
||||
obj->setScale(fontScaled);
|
||||
obj->setPosition(labelTrans);
|
||||
obj->setProperty("labelText", labels[i]);
|
||||
|
|
@ -6645,17 +6739,17 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
fontScaled.setX(scaleFactor * fontRatio);
|
||||
adjustment = labelsMaxWidth * scaleFactor;
|
||||
float xPos = 0.0f;
|
||||
if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row))
|
||||
if (isRow)
|
||||
xPos = backgroundScale.x() + (adjustment * 1.5f);
|
||||
else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column))
|
||||
else if (isColumn)
|
||||
xPos = backgroundScale.z() + (adjustment * 1.5f);
|
||||
labelTrans = QVector3D(xPos, 0.0f, 0.0f);
|
||||
QColor verticalLabelTextColor = theme()->axisY().labelTextColor();
|
||||
|
||||
if (verticalAxis->type() == QAbstract3DAxis::AxisType::Value) {
|
||||
auto valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
|
||||
for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) {
|
||||
auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(i));
|
||||
for (int i = 0; i < sliceVerticalLabelRepeater->count(); i++) {
|
||||
auto obj = static_cast<QQuick3DNode *>(sliceVerticalLabelRepeater->objectAt(i));
|
||||
labelTrans.setY(valueAxis->labelPositionAt(i) * scale * 2.0f - translate);
|
||||
obj->setScale(fontScaled);
|
||||
obj->setPosition(labelTrans);
|
||||
|
|
@ -6671,9 +6765,9 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
obj->setVisible(false);
|
||||
}
|
||||
} else if (verticalAxis->type() == QAbstract3DAxis::AxisType::Category) {
|
||||
for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) {
|
||||
for (int i = 0; i < sliceVerticalLabelRepeater->count(); i++) {
|
||||
labelTrans = calculateCategoryLabelPosition(verticalAxis, labelTrans, i);
|
||||
auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(i));
|
||||
auto obj = static_cast<QQuick3DNode *>(sliceVerticalLabelRepeater->objectAt(i));
|
||||
obj->setScale(fontScaled);
|
||||
obj->setPosition(labelTrans);
|
||||
obj->setProperty("labelText", labels[i]);
|
||||
|
|
@ -6692,26 +6786,31 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
QVector3D vTitleScale = fontScaled;
|
||||
vTitleScale.setX(fontScaled.y() * labelWidth / labelHeight);
|
||||
adjustment = labelHeight * scaleFactor;
|
||||
if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row))
|
||||
if (isRow)
|
||||
xPos = backgroundScale.x() + adjustment;
|
||||
else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column))
|
||||
else if (isColumn)
|
||||
xPos = backgroundScale.z() + adjustment;
|
||||
labelTrans = QVector3D(-(xPos + adjustment), 0.0f, 0.0f);
|
||||
|
||||
QQuick3DNode *sliceVerticalTitleLabel = nullptr;
|
||||
if (verticalTitle)
|
||||
sliceVerticalTitleLabel = verticalTitle;
|
||||
else
|
||||
sliceVerticalTitleLabel = m_sliceVerticalTitleLabel;
|
||||
if (!verticalAxis->title().isEmpty()) {
|
||||
m_sliceVerticalTitleLabel->setScale(vTitleScale);
|
||||
m_sliceVerticalTitleLabel->setPosition(labelTrans);
|
||||
m_sliceVerticalTitleLabel->setProperty("labelWidth", labelWidth);
|
||||
m_sliceVerticalTitleLabel->setProperty("labelHeight", labelHeight);
|
||||
m_sliceVerticalTitleLabel->setProperty("labelText", verticalAxis->title());
|
||||
m_sliceVerticalTitleLabel->setProperty("labelFont", font);
|
||||
m_sliceVerticalTitleLabel->setProperty("borderVisible", borderVisible);
|
||||
m_sliceVerticalTitleLabel->setProperty("labelTextColor", verticalLabelTextColor);
|
||||
m_sliceVerticalTitleLabel->setProperty("backgroundVisible", backgroundVisible);
|
||||
m_sliceVerticalTitleLabel->setProperty("backgroundColor", backgroundColor);
|
||||
m_sliceVerticalTitleLabel->setEulerRotation(QVector3D(.0f, .0f, 90.0f));
|
||||
sliceVerticalTitleLabel->setScale(vTitleScale);
|
||||
sliceVerticalTitleLabel->setPosition(labelTrans);
|
||||
sliceVerticalTitleLabel->setProperty("labelWidth", labelWidth);
|
||||
sliceVerticalTitleLabel->setProperty("labelHeight", labelHeight);
|
||||
sliceVerticalTitleLabel->setProperty("labelText", verticalAxis->title());
|
||||
sliceVerticalTitleLabel->setProperty("labelFont", font);
|
||||
sliceVerticalTitleLabel->setProperty("borderVisible", borderVisible);
|
||||
sliceVerticalTitleLabel->setProperty("labelTextColor", verticalLabelTextColor);
|
||||
sliceVerticalTitleLabel->setProperty("backgroundVisible", backgroundVisible);
|
||||
sliceVerticalTitleLabel->setProperty("backgroundColor", backgroundColor);
|
||||
sliceVerticalTitleLabel->setEulerRotation(QVector3D(.0f, .0f, 90.0f));
|
||||
} else {
|
||||
m_sliceVerticalTitleLabel->setVisible(false);
|
||||
sliceVerticalTitleLabel->setVisible(false);
|
||||
}
|
||||
|
||||
labelHeight = fm.height() + textPadding;
|
||||
|
|
@ -6722,26 +6821,36 @@ void QQuickGraphsItem::updateSliceLabels()
|
|||
yPos = backgroundScale.y() * 1.5f + (adjustment * 6.f);
|
||||
labelTrans = QVector3D(0.0f, -yPos, 0.0f);
|
||||
|
||||
QQuick3DNode *sliceHorizontalTitleLabel = nullptr;
|
||||
if (horizontalTitle)
|
||||
sliceHorizontalTitleLabel = horizontalTitle;
|
||||
else
|
||||
sliceHorizontalTitleLabel = m_sliceHorizontalTitleLabel;
|
||||
if (!horizontalAxis->title().isEmpty()) {
|
||||
m_sliceHorizontalTitleLabel->setScale(hTitleScale);
|
||||
m_sliceHorizontalTitleLabel->setPosition(labelTrans);
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelWidth", labelWidth);
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelHeight", labelHeight);
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelText", horizontalAxis->title());
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelFont", font);
|
||||
m_sliceHorizontalTitleLabel->setProperty("borderVisible", borderVisible);
|
||||
m_sliceHorizontalTitleLabel->setProperty("labelTextColor", horizontalLabelTextColor);
|
||||
m_sliceHorizontalTitleLabel->setProperty("backgroundVisible", backgroundVisible);
|
||||
m_sliceHorizontalTitleLabel->setProperty("backgroundColor", backgroundColor);
|
||||
sliceHorizontalTitleLabel->setScale(hTitleScale);
|
||||
sliceHorizontalTitleLabel->setPosition(labelTrans);
|
||||
sliceHorizontalTitleLabel->setProperty("labelWidth", labelWidth);
|
||||
sliceHorizontalTitleLabel->setProperty("labelHeight", labelHeight);
|
||||
sliceHorizontalTitleLabel->setProperty("labelText", horizontalAxis->title());
|
||||
sliceHorizontalTitleLabel->setProperty("labelFont", font);
|
||||
sliceHorizontalTitleLabel->setProperty("borderVisible", borderVisible);
|
||||
sliceHorizontalTitleLabel->setProperty("labelTextColor", horizontalLabelTextColor);
|
||||
sliceHorizontalTitleLabel->setProperty("backgroundVisible", backgroundVisible);
|
||||
sliceHorizontalTitleLabel->setProperty("backgroundColor", backgroundColor);
|
||||
} else {
|
||||
m_sliceHorizontalTitleLabel->setVisible(false);
|
||||
sliceHorizontalTitleLabel->setVisible(false);
|
||||
}
|
||||
|
||||
m_sliceItemLabel->setProperty("labelFont", font);
|
||||
m_sliceItemLabel->setProperty("borderVisible", borderVisible);
|
||||
m_sliceItemLabel->setProperty("labelTextColor", theme()->labelTextColor());
|
||||
m_sliceItemLabel->setProperty("backgroundVisible", backgroundVisible);
|
||||
m_sliceItemLabel->setProperty("backgroundColor", backgroundColor);
|
||||
QQuick3DNode *sliceItemLabel = nullptr;
|
||||
if (itemLabel)
|
||||
sliceItemLabel = itemLabel;
|
||||
else
|
||||
sliceItemLabel = m_sliceItemLabel;
|
||||
sliceItemLabel->setProperty("labelFont", font);
|
||||
sliceItemLabel->setProperty("borderVisible", borderVisible);
|
||||
sliceItemLabel->setProperty("labelTextColor", theme()->labelTextColor());
|
||||
sliceItemLabel->setProperty("backgroundVisible", backgroundVisible);
|
||||
sliceItemLabel->setProperty("backgroundColor", backgroundColor);
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::setUpCamera()
|
||||
|
|
|
|||
|
|
@ -279,13 +279,6 @@ public:
|
|||
m_graphPositionQueryPending = pending;
|
||||
}
|
||||
|
||||
enum SelectionType {
|
||||
SelectionNone = 0,
|
||||
SelectionItem,
|
||||
SelectionRow,
|
||||
SelectionColumn,
|
||||
};
|
||||
|
||||
virtual void addSeriesInternal(QAbstract3DSeries *series);
|
||||
void insertSeries(qsizetype index, QAbstract3DSeries *series);
|
||||
virtual void removeSeriesInternal(QAbstract3DSeries *series);
|
||||
|
|
@ -665,6 +658,7 @@ protected:
|
|||
};
|
||||
|
||||
virtual void createSliceView();
|
||||
QQuick3DViewport *createOffscreenSliceView(QtGraphs3D::SliceType sliceType);
|
||||
|
||||
void handleQueryPositionChanged(QPoint position);
|
||||
|
||||
|
|
@ -715,8 +709,14 @@ protected:
|
|||
void updateGrid();
|
||||
void updateGridLineType();
|
||||
void updateLabels();
|
||||
void updateSliceGrid();
|
||||
void updateSliceLabels();
|
||||
void updateSliceGrid(QQuick3DModel *sliceGrid = nullptr,
|
||||
QtGraphs3D::SliceType selectedFlag = QtGraphs3D::SliceType::SliceNone);
|
||||
void updateSliceLabels(QQuick3DRepeater *horizontalLabel = nullptr,
|
||||
QQuick3DRepeater *verticalLabel = nullptr,
|
||||
QQuick3DNode *horizontalTitle = nullptr,
|
||||
QQuick3DNode *verticalTitle = nullptr,
|
||||
QQuick3DNode *itemLabel = nullptr,
|
||||
QtGraphs3D::SliceType selectedFlag = QtGraphs3D::SliceType::SliceNone);
|
||||
void updateBackgroundColor();
|
||||
void setItemSelected(bool selected);
|
||||
virtual void updateShadowQuality(QtGraphs3D::ShadowQuality quality);
|
||||
|
|
@ -748,7 +748,7 @@ protected:
|
|||
void setSliceEnabled(bool enabled) { m_sliceEnabled = enabled; }
|
||||
bool isSliceActivatedChanged() const { return m_sliceActivatedChanged; }
|
||||
virtual void toggleSliceGraph();
|
||||
void createSliceCamera();
|
||||
void createSliceCamera(QQuick3DViewport *sliceView);
|
||||
bool isSliceOrthoProjection() const { return m_sliceUseOrthoProjection; }
|
||||
void setSliceOrthoProjection(bool enable) { m_sliceUseOrthoProjection = enable; }
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
|
||||
#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
|
||||
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
|
|
@ -118,6 +120,23 @@ QT_BEGIN_NAMESPACE
|
|||
* \sa GraphsItem3D::hasSeries()
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod void Surface3D::removeSeries(Surface3DSeries series)
|
||||
* Removes the \a series from the graph.
|
||||
* \sa GraphsItem3D::hasSeries()
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlmethod void Surface3D::renderSliceToImage(int index, int requestedIndex, QtGraphs3D::SliceType sliceType, QUrl filePath)
|
||||
* \since 6.10
|
||||
*
|
||||
* Exports a 2d slice from series at \a index and saves the result to an image
|
||||
* at a specified \a filePath.
|
||||
* To export all series, set \a index to -1.
|
||||
* The exported slice includes lines of row or column, which is defined by
|
||||
* \a sliceType at a given \a requestedIndex.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlsignal Surface3D::axisXChanged(ValueAxis3D axis)
|
||||
*
|
||||
|
|
@ -2613,6 +2632,220 @@ void QQuickGraphsSurface::createSliceView()
|
|||
}
|
||||
}
|
||||
|
||||
QQuick3DViewport *QQuickGraphsSurface::createOffscreenSliceView(int index, int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
QQuick3DViewport *sliceView = QQuickGraphsItem::createOffscreenSliceView(sliceType);
|
||||
|
||||
bool isRow = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceRow);
|
||||
bool isColumn = (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
|
||||
|| sliceType == QtGraphs3D::SliceType::SliceColumn);
|
||||
|
||||
int modelIndex = 0;
|
||||
for (const auto &model : std::as_const(m_model)) {
|
||||
if (index > 0 && modelIndex++ != index)
|
||||
continue;
|
||||
|
||||
QRect sampleSpace = model->sampleSpace;
|
||||
int rowStart = sampleSpace.top();
|
||||
int columnStart = sampleSpace.left();
|
||||
int rowEnd = sampleSpace.bottom() + 1;
|
||||
int columnEnd = sampleSpace.right() + 1;
|
||||
int rowCount = sampleSpace.height();
|
||||
int columnCount = sampleSpace.width();
|
||||
|
||||
QVector<SurfaceVertex> selectedSeries;
|
||||
int indexCount = 0;
|
||||
const QSurfaceDataArray &array = model->series->dataArray();
|
||||
const qsizetype maxRow = array.size() - 1;
|
||||
const qsizetype maxColumn = array.at(0).size() - 1;
|
||||
const bool ascendingX = array.at(0).at(0).x() < array.at(0).at(maxColumn).x();
|
||||
const bool ascendingZ = array.at(0).at(0).z() < array.at(maxRow).at(0).z();
|
||||
|
||||
if (requestedIndex < 0 || requestedIndex >= maxRow || requestedIndex >= maxColumn) {
|
||||
qWarning("The index is out of range. The render stops.");
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isRow && requestedIndex != -1) {
|
||||
selectedSeries.reserve(columnCount * 2);
|
||||
QVector<SurfaceVertex> list;
|
||||
QSurfaceDataRow row = array.at(requestedIndex);
|
||||
for (int i = columnStart; i < columnEnd; i++) {
|
||||
int index = ascendingX ? i : columnEnd - i + columnStart - 1;
|
||||
QVector3D pos = getNormalizedVertex(row.at(index), false, false);
|
||||
SurfaceVertex vertex;
|
||||
vertex.position = pos;
|
||||
vertex.position.setY(vertex.position.y() - .025f);
|
||||
vertex.position.setZ(.0f);
|
||||
selectedSeries.append(vertex);
|
||||
vertex.position.setY(vertex.position.y() + .05f);
|
||||
list.append(vertex);
|
||||
}
|
||||
selectedSeries.append(list);
|
||||
indexCount = columnCount - 1;
|
||||
}
|
||||
|
||||
if (isColumn && requestedIndex != -1) {
|
||||
selectedSeries.reserve(rowCount * 2);
|
||||
QVector<SurfaceVertex> list;
|
||||
for (int i = rowStart; i < rowEnd; i++) {
|
||||
int index = ascendingZ ? i : rowEnd - i + rowStart - 1;
|
||||
QVector3D pos =
|
||||
getNormalizedVertex(array.at(index).at(requestedIndex), false, false);
|
||||
SurfaceVertex vertex;
|
||||
vertex.position = pos;
|
||||
vertex.position.setX(-vertex.position.z());
|
||||
vertex.position.setY(vertex.position.y() - .025f);
|
||||
vertex.position.setZ(0);
|
||||
selectedSeries.append(vertex);
|
||||
vertex.position.setY(vertex.position.y() + .05f);
|
||||
list.append(vertex);
|
||||
}
|
||||
selectedSeries.append(list);
|
||||
indexCount = rowCount - 1;
|
||||
|
||||
QQmlListReference materialRef(model->sliceModel, "materials");
|
||||
auto material = materialRef.at(0);
|
||||
material->setProperty("isColumn", true);
|
||||
}
|
||||
|
||||
QVector<quint32> indices;
|
||||
indices.reserve(indexCount * 6);
|
||||
for (int i = 0; i < indexCount; i++) {
|
||||
indices.push_back(i + 1);
|
||||
indices.push_back(i + indexCount + 1);
|
||||
indices.push_back(i);
|
||||
indices.push_back(i + indexCount + 2);
|
||||
indices.push_back(i + indexCount + 1);
|
||||
indices.push_back(i + 1);
|
||||
}
|
||||
|
||||
auto surfaceModel = new QQuick3DModel();
|
||||
surfaceModel->setParent(sliceView->scene());
|
||||
surfaceModel->setParentItem(sliceView->scene());
|
||||
|
||||
auto geometry = new QQuick3DGeometry();
|
||||
geometry->setParent(surfaceModel);
|
||||
geometry->setParentItem(surfaceModel);
|
||||
geometry->setStride(sizeof(SurfaceVertex));
|
||||
geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
|
||||
geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic, sizeof(QVector3D),
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
geometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0,
|
||||
QQuick3DGeometry::Attribute::U32Type);
|
||||
QByteArray vertexBuffer(reinterpret_cast<char *>(selectedSeries.data()),
|
||||
selectedSeries.size() * sizeof(SurfaceVertex));
|
||||
geometry->setVertexData(vertexBuffer);
|
||||
QByteArray indexBuffer(reinterpret_cast<char *>(indices.data()),
|
||||
indices.size() * sizeof(quint32));
|
||||
geometry->setIndexData(indexBuffer);
|
||||
surfaceModel->setGeometry(geometry);
|
||||
|
||||
QQmlListReference materialRef(surfaceModel, "materials");
|
||||
auto material = createQmlCustomMaterial(QStringLiteral(":/materials/SurfaceSliceMaterial"));
|
||||
material->setCullMode(QQuick3DMaterial::NoCulling);
|
||||
QVariant textureInputAsVariant = material->property("custex");
|
||||
QQuick3DShaderUtilsTextureInput *textureInput =
|
||||
textureInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
|
||||
QQuick3DTexture *texture = model->texture;
|
||||
textureInput->setTexture(texture);
|
||||
materialRef.append(material);
|
||||
|
||||
if (model->series->drawMode().testFlag(QSurface3DSeries::DrawSurface))
|
||||
surfaceModel->setLocalOpacity(1.f);
|
||||
else
|
||||
surfaceModel->setLocalOpacity(.0f);
|
||||
|
||||
if (model->series->drawMode().testFlag(QSurface3DSeries::DrawWireframe)) {
|
||||
QVector<quint32> gridIndices;
|
||||
gridIndices.reserve(indexCount * 4);
|
||||
for (int i = 0; i < indexCount; i++) {
|
||||
gridIndices.push_back(i);
|
||||
gridIndices.push_back(i + indexCount + 1);
|
||||
|
||||
gridIndices.push_back(i);
|
||||
gridIndices.push_back(i + 1);
|
||||
}
|
||||
QQuick3DModel *gridModel = new QQuick3DModel();
|
||||
gridModel->setParent(sliceView->scene());
|
||||
gridModel->setParentItem(sliceView->scene());
|
||||
gridModel->setDepthBias(1.0f);
|
||||
QQuick3DGeometry *gridGeometry = new QQuick3DGeometry();
|
||||
gridGeometry->setParent(gridModel);
|
||||
gridGeometry->setStride(sizeof(SurfaceVertex));
|
||||
gridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
|
||||
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0,
|
||||
QQuick3DGeometry::Attribute::U32Type);
|
||||
QByteArray gridIndexBuffer(reinterpret_cast<char *>(gridIndices.data()),
|
||||
gridIndices.size() * sizeof(quint32));
|
||||
gridGeometry->setVertexData(vertexBuffer);
|
||||
gridGeometry->setIndexData(gridIndexBuffer);
|
||||
gridGeometry->update();
|
||||
gridModel->setGeometry(gridGeometry);
|
||||
QQmlListReference gridMaterialRef(gridModel, "materials");
|
||||
QQuick3DPrincipledMaterial *gridMaterial = new QQuick3DPrincipledMaterial();
|
||||
gridMaterial->setParent(gridModel);
|
||||
gridMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
|
||||
gridMaterial->setParent(gridModel);
|
||||
QColor gridColor = model->series->wireframeColor();
|
||||
gridMaterial->setBaseColor(gridColor);
|
||||
gridMaterialRef.append(gridMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
return sliceView;
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> QQuickGraphsSurface::renderSliceToImage(
|
||||
int index, int requestedIndex, QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
QQuick3DViewport *sliceView = createOffscreenSliceView(index, requestedIndex, sliceType);
|
||||
|
||||
if (!sliceView) {
|
||||
return QSharedPointer<QQuickItemGrabResult>();
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
|
||||
connect(grabbed.data(), &QQuickItemGrabResult::ready, this, [sliceView]() {
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
});
|
||||
|
||||
return grabbed;
|
||||
}
|
||||
|
||||
void QQuickGraphsSurface::renderSliceToImage(int index, int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType, const QUrl &filePath)
|
||||
{
|
||||
QQuick3DViewport *sliceView = createOffscreenSliceView(index, requestedIndex, sliceType);
|
||||
|
||||
if (!sliceView)
|
||||
return;
|
||||
|
||||
if (filePath.isEmpty()) {
|
||||
qWarning("Save path is not defined.");
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
|
||||
connect(grabbed.data(), &QQuickItemGrabResult::ready, this, [grabbed, sliceView, filePath]() {
|
||||
if (!grabbed.data()->saveToFile(filePath))
|
||||
qWarning("Saving requested slice view to image failed");
|
||||
sliceView->setVisible(false);
|
||||
sliceView->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void QQuickGraphsSurface::updateSliceItemLabel(const QString &label, QVector3D position)
|
||||
{
|
||||
QQuickGraphsItem::updateSliceItemLabel(label, position);
|
||||
|
|
@ -2657,7 +2890,9 @@ void QQuickGraphsSurface::updateSelectionMode(QtGraphs3D::SelectionFlags mode)
|
|||
|
||||
void QQuickGraphsSurface::addSliceModel(SurfaceModel *model)
|
||||
{
|
||||
QQuick3DViewport *sliceParent = sliceView();
|
||||
QQuick3DViewport *sliceParent = nullptr;
|
||||
|
||||
sliceParent = sliceView();
|
||||
|
||||
auto surfaceModel = new QQuick3DModel();
|
||||
surfaceModel->setParent(sliceParent->scene());
|
||||
|
|
|
|||
|
|
@ -144,6 +144,11 @@ public:
|
|||
void handleSeriesVisibilityChangedBySender(QObject *sender) override;
|
||||
void adjustAxisRanges() override;
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> renderSliceToImage(int index, int requestedIndex, QtGraphs3D::SliceType sliceType);
|
||||
Q_REVISION(6, 10)
|
||||
Q_INVOKABLE void renderSliceToImage(int index, int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType, const QUrl &filePath);
|
||||
|
||||
protected:
|
||||
void componentComplete() override;
|
||||
void synchData() override;
|
||||
|
|
@ -160,6 +165,9 @@ protected:
|
|||
void updateSliceItemLabel(const QString &label, QVector3D position) override;
|
||||
void updateSelectionMode(QtGraphs3D::SelectionFlags mode) override;
|
||||
|
||||
QQuick3DViewport *createOffscreenSliceView(int index, int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType);
|
||||
|
||||
public Q_SLOTS:
|
||||
void handleAxisXChanged(QAbstract3DAxis *axis) override;
|
||||
void handleAxisYChanged(QAbstract3DAxis *axis) override;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,20 @@ QT_BEGIN_NAMESPACE
|
|||
Multi-series selection is not supported for Q3DScatterWidgetItem.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QtGraphs3D::SliceType
|
||||
\since 6.10
|
||||
|
||||
Type of slice to grab to an image.
|
||||
|
||||
\value SliceNone
|
||||
Slice type is not defined.
|
||||
\value SliceRow
|
||||
Slice for rows.
|
||||
\value SliceColumn
|
||||
Slice for columns.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QtGraphs3D::ShadowQuality
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,13 @@ enum class SelectionFlag {
|
|||
Q_FLAG_NS(SelectionFlag)
|
||||
Q_DECLARE_FLAGS(SelectionFlags, SelectionFlag)
|
||||
|
||||
enum class SliceType {
|
||||
SliceNone,
|
||||
SliceRow,
|
||||
SliceColumn,
|
||||
};
|
||||
Q_ENUM_NS(SliceType)
|
||||
|
||||
enum class ShadowQuality {
|
||||
None,
|
||||
Low,
|
||||
|
|
|
|||
|
|
@ -449,4 +449,22 @@ const QQuickGraphsBars *Q3DBarsWidgetItem::graphBars() const
|
|||
return static_cast<const QQuickGraphsBars *>(d->m_graphsItem.get());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Exports the requested slice view to an image.
|
||||
* The exported slice is bars of row or column, which is defined by \a sliceType,
|
||||
* at a given \a requestedIndex.
|
||||
* Returns a shared pointer to grab result which can be used to access the
|
||||
* exported image when it's ready. Image is rendered with the current
|
||||
* antialiasing settings.
|
||||
*
|
||||
* \sa QQuickItem::grabToImage()
|
||||
*
|
||||
* \since 6.10
|
||||
*/
|
||||
QSharedPointer<QQuickItemGrabResult>
|
||||
Q3DBarsWidgetItem::renderSliceToImage(int requestedIndex, QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
return graphBars()->renderSliceToImage(requestedIndex, sliceType);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ public:
|
|||
void setFloorLevel(float level);
|
||||
float floorLevel() const;
|
||||
|
||||
Q_REVISION(6, 10)
|
||||
QSharedPointer<QQuickItemGrabResult> renderSliceToImage(int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <QtGraphsWidgets/q3dsurfacewidgetitem.h>
|
||||
#include <private/q3dsurfacewidgetitem_p.h>
|
||||
#include <private/qquickgraphssurface_p.h>
|
||||
#include "q3dsurfacewidgetitem.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -284,6 +285,27 @@ void Q3DSurfaceWidgetItem::releaseAxis(QValue3DAxis *axis)
|
|||
return graphSurface()->releaseAxis(axis);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Exports the requested slice view to an image.
|
||||
* The sliced result is the series of \a index. To export all series, set
|
||||
* \a index to -1.
|
||||
* The exported slice is a line of row or column, which is defined by \a sliceType,
|
||||
* at a given \a requestedIndex.
|
||||
* Returns a shared pointer to grab result which can be used to access the
|
||||
* exported image when it's ready. Image is rendered with the current
|
||||
* antialiasing settings.
|
||||
*
|
||||
* \sa QQuickItem::grabToImage()
|
||||
*
|
||||
* \since 6.10
|
||||
*/
|
||||
QSharedPointer<QQuickItemGrabResult>
|
||||
Q3DSurfaceWidgetItem::renderSliceToImage(int index, int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType)
|
||||
{
|
||||
return graphSurface()->renderSliceToImage(index, requestedIndex, sliceType);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the list of all added axes.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ public:
|
|||
void setFlipHorizontalGrid(bool flip);
|
||||
bool flipHorizontalGrid() const;
|
||||
|
||||
Q_REVISION(6, 10)
|
||||
QSharedPointer<QQuickItemGrabResult> renderSliceToImage(int index, int requestedIndex,
|
||||
QtGraphs3D::SliceType sliceType);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ if(QT_FEATURE_graphs_3d)
|
|||
endif()
|
||||
if(QT_FEATURE_graphs_3d_bars3d AND QT_FEATURE_graphs_3d_surface3d)
|
||||
add_subdirectory(qmltheme)
|
||||
add_subdirectory(qmlrenderslicetoimage)
|
||||
endif()
|
||||
if(QT_FEATURE_graphs_3d_bars3d AND QT_FEATURE_graphs_3d_scatter3d AND QT_FEATURE_graphs_3d_surface3d)
|
||||
add_subdirectory(qmlmultitest)
|
||||
|
|
@ -65,6 +66,7 @@ if(QT_FEATURE_graphs_3d)
|
|||
if(QT_FEATURE_graphs_3d_bars3d AND QT_FEATURE_graphs_3d_surface3d)
|
||||
add_subdirectory(itemmodel)
|
||||
add_subdirectory(itemmodeltest)
|
||||
add_subdirectory(renderslicetoimage)
|
||||
endif()
|
||||
if(QT_FEATURE_graphs_3d_bars3d AND QT_FEATURE_graphs_3d_scatter3d AND QT_FEATURE_graphs_3d_surface3d)
|
||||
add_subdirectory(multigraphs)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ qtHaveModule(quick) {
|
|||
qmlcustominput \
|
||||
qmllegend \
|
||||
qmlsurfacelayers \
|
||||
qmltestbed
|
||||
qmltestbed \
|
||||
qmlrenderslicetoimage
|
||||
}
|
||||
|
||||
!android:!ios:!winrt {
|
||||
|
|
@ -24,7 +25,8 @@ qtHaveModule(quick) {
|
|||
volumetrictest \
|
||||
rotations \
|
||||
custominput \
|
||||
itemmodel
|
||||
itemmodel \
|
||||
renderslicetoimage
|
||||
|
||||
# For testing code snippets of minimal applications
|
||||
SUBDIRS += minimalbars \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (C) 2025 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||
project(tst_qmlrenderslicetoimage LANGUAGES C CXX ASM)
|
||||
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
|
||||
endif()
|
||||
|
||||
qt_internal_add_manual_test(tst_qmlrenderslicetoimage
|
||||
GUI
|
||||
SOURCES
|
||||
main.cpp
|
||||
)
|
||||
target_link_libraries(tst_qmlrenderslicetoimage PUBLIC
|
||||
Qt::Gui
|
||||
Qt::Graphs
|
||||
)
|
||||
|
||||
set(qmlrenderslicetoimage_resource_files
|
||||
"qml/qmlrenderslicetoimage/main.qml"
|
||||
"qml/qmlrenderslicetoimage/SurfaceGraph.qml"
|
||||
"qml/qmlrenderslicetoimage/BarGraph.qml"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qmlrenderslicetoimage "qmlgrabsliceview"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${qmlrenderslicetoimage_resource_files}
|
||||
)
|
||||
|
||||
set(qmlrenderslicetoimage_image_resource_files
|
||||
"layer_1.png"
|
||||
"layer_2.png"
|
||||
"layer_3.png"
|
||||
)
|
||||
|
||||
qt6_add_resources(tst_qmlrenderslicetoimage "qmlsurfacelayers1"
|
||||
PREFIX
|
||||
"/heightmaps"
|
||||
FILES
|
||||
${qmlrenderslicetoimage_image_resource_files}
|
||||
)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtQuick/QQuickView>
|
||||
#include <QtQml/QQmlEngine>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQuickView viewer;
|
||||
|
||||
// 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
|
||||
viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
|
||||
QString::fromLatin1("qml")));
|
||||
QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close);
|
||||
|
||||
viewer.setTitle(QStringLiteral("QML Grab Slice View Test"));
|
||||
|
||||
viewer.setSource(QUrl("qrc:/qml/qmlrenderslicetoimage/main.qml"));
|
||||
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
viewer.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtGraphs
|
||||
|
||||
Item {
|
||||
property var graph: barGraph
|
||||
Bars3D {
|
||||
id: barGraph
|
||||
anchors.fill: parent
|
||||
shadowQuality: Graphs3D.ShadowQuality.SoftHigh
|
||||
selectionMode: Graphs3D.SelectionFlag.Row | Graphs3D.SelectionFlag.Slice
|
||||
theme: GraphsTheme {
|
||||
colorScheme: GraphsTheme.ColorScheme.Dark
|
||||
labelBorderVisible: true
|
||||
labelFont.pointSize: 35
|
||||
labelBackgroundVisible: true
|
||||
colorStyle: GraphsTheme.ColorStyle.RangeGradient
|
||||
singleHighlightGradient: customGradient
|
||||
|
||||
Gradient {
|
||||
id: customGradient
|
||||
GradientStop { position: 1.0; color: "#FFFF00" }
|
||||
GradientStop { position: 0.0; color: "#808000" }
|
||||
}
|
||||
}
|
||||
barThickness: 0.7
|
||||
barSpacing: Qt.size(0.5, 0.5)
|
||||
barSpacingRelative: false
|
||||
cameraPreset: Graphs3D.CameraPreset.IsometricLeftHigh
|
||||
columnAxis: graphAxes.column
|
||||
rowAxis: graphAxes.row
|
||||
valueAxis: graphAxes.value
|
||||
|
||||
Bar3DSeries {
|
||||
id: secondarySeries
|
||||
visible: true
|
||||
itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
|
||||
baseGradient: secondaryGradient
|
||||
|
||||
ItemModelBarDataProxy {
|
||||
id: secondaryProxy
|
||||
itemModel: graphData.model
|
||||
rowRole: "timestamp"
|
||||
columnRole: "timestamp"
|
||||
valueRole: "expenses"
|
||||
rowRolePattern: /^(\d\d\d\d).*$/
|
||||
columnRolePattern: /^.*-(\d\d)$/
|
||||
valueRolePattern: /-/
|
||||
rowRoleReplace: "\\1"
|
||||
columnRoleReplace: "\\1"
|
||||
multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: secondaryGradient
|
||||
GradientStop { position: 1.0; color: "#FF0000" }
|
||||
GradientStop { position: 0.0; color: "#600000" }
|
||||
}
|
||||
}
|
||||
|
||||
Bar3DSeries {
|
||||
id: barSeries
|
||||
itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel"
|
||||
baseGradient: barGradient
|
||||
|
||||
ItemModelBarDataProxy {
|
||||
id: modelProxy
|
||||
itemModel: graphData.model
|
||||
rowRole: "timestamp"
|
||||
columnRole: "timestamp"
|
||||
valueRole: "income"
|
||||
rowRolePattern: /^(\d\d\d\d).*$/
|
||||
columnRolePattern: /^.*-(\d\d)$/
|
||||
rowRoleReplace: "\\1"
|
||||
columnRoleReplace: "\\1"
|
||||
multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: barGradient
|
||||
GradientStop { position: 1.0; color: "#00FF00" }
|
||||
GradientStop { position: 0.0; color: "#006000" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: graphAxes
|
||||
property alias column: columnAxis
|
||||
property alias row: rowAxis
|
||||
property alias value: valueAxis
|
||||
property alias total: totalAxis
|
||||
|
||||
// Custom labels for columns, since the data contains abbreviated month names.
|
||||
Category3DAxis {
|
||||
id: columnAxis
|
||||
labels: ["January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December"]
|
||||
labelAutoAngle: 30
|
||||
}
|
||||
Category3DAxis {
|
||||
id: totalAxis
|
||||
labels: ["Yearly total"]
|
||||
labelAutoAngle: 30
|
||||
}
|
||||
Category3DAxis {
|
||||
// For row labels we can use row labels from data proxy, no labels defined for rows.
|
||||
id: rowAxis
|
||||
labelAutoAngle: 30
|
||||
}
|
||||
|
||||
Value3DAxis {
|
||||
id: valueAxis
|
||||
min: 0
|
||||
max: 35
|
||||
labelFormat: "%.2f M\u20AC"
|
||||
title: "Monthly income"
|
||||
labelAutoAngle: 90
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: graphData
|
||||
|
||||
property alias model: dataModel
|
||||
|
||||
property var modelAsJsArray: {
|
||||
var arr = [];
|
||||
for (var i = 0; i < dataModel.count; i++) {
|
||||
var row = dataModel.get(i);
|
||||
arr.push({
|
||||
timestamp: row.timestamp,
|
||||
expenses: row.expenses,
|
||||
income: row.income
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: dataModel
|
||||
ListElement{ timestamp: "2016-01"; expenses: "-4"; income: "5" }
|
||||
ListElement{ timestamp: "2016-02"; expenses: "-5"; income: "6" }
|
||||
ListElement{ timestamp: "2016-03"; expenses: "-7"; income: "4" }
|
||||
ListElement{ timestamp: "2016-04"; expenses: "-3"; income: "2" }
|
||||
ListElement{ timestamp: "2016-05"; expenses: "-4"; income: "1" }
|
||||
ListElement{ timestamp: "2016-06"; expenses: "-2"; income: "2" }
|
||||
ListElement{ timestamp: "2016-07"; expenses: "-1"; income: "3" }
|
||||
ListElement{ timestamp: "2016-08"; expenses: "-5"; income: "1" }
|
||||
ListElement{ timestamp: "2016-09"; expenses: "-2"; income: "3" }
|
||||
ListElement{ timestamp: "2016-10"; expenses: "-5"; income: "2" }
|
||||
ListElement{ timestamp: "2016-11"; expenses: "-8"; income: "5" }
|
||||
ListElement{ timestamp: "2016-12"; expenses: "-3"; income: "3" }
|
||||
|
||||
ListElement{ timestamp: "2017-01"; expenses: "-3"; income: "1" }
|
||||
ListElement{ timestamp: "2017-02"; expenses: "-4"; income: "2" }
|
||||
ListElement{ timestamp: "2017-03"; expenses: "-12"; income: "4" }
|
||||
ListElement{ timestamp: "2017-04"; expenses: "-13"; income: "6" }
|
||||
ListElement{ timestamp: "2017-05"; expenses: "-14"; income: "11" }
|
||||
ListElement{ timestamp: "2017-06"; expenses: "-7"; income: "7" }
|
||||
ListElement{ timestamp: "2017-07"; expenses: "-6"; income: "4" }
|
||||
ListElement{ timestamp: "2017-08"; expenses: "-4"; income: "15" }
|
||||
ListElement{ timestamp: "2017-09"; expenses: "-2"; income: "18" }
|
||||
ListElement{ timestamp: "2017-10"; expenses: "-29"; income: "25" }
|
||||
ListElement{ timestamp: "2017-11"; expenses: "-23"; income: "29" }
|
||||
ListElement{ timestamp: "2017-12"; expenses: "-5"; income: "9" }
|
||||
|
||||
ListElement{ timestamp: "2018-01"; expenses: "-3"; income: "8" }
|
||||
ListElement{ timestamp: "2018-02"; expenses: "-8"; income: "14" }
|
||||
ListElement{ timestamp: "2018-03"; expenses: "-10"; income: "20" }
|
||||
ListElement{ timestamp: "2018-04"; expenses: "-12"; income: "24" }
|
||||
ListElement{ timestamp: "2018-05"; expenses: "-10"; income: "19" }
|
||||
ListElement{ timestamp: "2018-06"; expenses: "-5"; income: "8" }
|
||||
ListElement{ timestamp: "2018-07"; expenses: "-1"; income: "4" }
|
||||
ListElement{ timestamp: "2018-08"; expenses: "-7"; income: "12" }
|
||||
ListElement{ timestamp: "2018-09"; expenses: "-4"; income: "16" }
|
||||
ListElement{ timestamp: "2018-10"; expenses: "-22"; income: "33" }
|
||||
ListElement{ timestamp: "2018-11"; expenses: "-16"; income: "25" }
|
||||
ListElement{ timestamp: "2018-12"; expenses: "-2"; income: "7" }
|
||||
|
||||
ListElement{ timestamp: "2019-01"; expenses: "-4"; income: "5" }
|
||||
ListElement{ timestamp: "2019-02"; expenses: "-4"; income: "7" }
|
||||
ListElement{ timestamp: "2019-03"; expenses: "-11"; income: "14" }
|
||||
ListElement{ timestamp: "2019-04"; expenses: "-16"; income: "22" }
|
||||
ListElement{ timestamp: "2019-05"; expenses: "-3"; income: "5" }
|
||||
ListElement{ timestamp: "2019-06"; expenses: "-4"; income: "8" }
|
||||
ListElement{ timestamp: "2019-07"; expenses: "-7"; income: "9" }
|
||||
ListElement{ timestamp: "2019-08"; expenses: "-9"; income: "13" }
|
||||
ListElement{ timestamp: "2019-09"; expenses: "-1"; income: "6" }
|
||||
ListElement{ timestamp: "2019-10"; expenses: "-14"; income: "25" }
|
||||
ListElement{ timestamp: "2019-11"; expenses: "-19"; income: "29" }
|
||||
ListElement{ timestamp: "2019-12"; expenses: "-5"; income: "7" }
|
||||
|
||||
ListElement{ timestamp: "2020-01"; expenses: "-14"; income: "22" }
|
||||
ListElement{ timestamp: "2020-02"; expenses: "-5"; income: "7" }
|
||||
ListElement{ timestamp: "2020-03"; expenses: "-1"; income: "9" }
|
||||
ListElement{ timestamp: "2020-04"; expenses: "-1"; income: "12" }
|
||||
ListElement{ timestamp: "2020-05"; expenses: "-5"; income: "9" }
|
||||
ListElement{ timestamp: "2020-06"; expenses: "-5"; income: "8" }
|
||||
ListElement{ timestamp: "2020-07"; expenses: "-3"; income: "7" }
|
||||
ListElement{ timestamp: "2020-08"; expenses: "-1"; income: "5" }
|
||||
ListElement{ timestamp: "2020-09"; expenses: "-2"; income: "4" }
|
||||
ListElement{ timestamp: "2020-10"; expenses: "-10"; income: "13" }
|
||||
ListElement{ timestamp: "2020-11"; expenses: "-12"; income: "17" }
|
||||
ListElement{ timestamp: "2020-12"; expenses: "-6"; income: "9" }
|
||||
|
||||
ListElement{ timestamp: "2021-01"; expenses: "-2"; income: "6" }
|
||||
ListElement{ timestamp: "2021-02"; expenses: "-4"; income: "8" }
|
||||
ListElement{ timestamp: "2021-03"; expenses: "-7"; income: "12" }
|
||||
ListElement{ timestamp: "2021-04"; expenses: "-9"; income: "15" }
|
||||
ListElement{ timestamp: "2021-05"; expenses: "-7"; income: "19" }
|
||||
ListElement{ timestamp: "2021-06"; expenses: "-9"; income: "18" }
|
||||
ListElement{ timestamp: "2021-07"; expenses: "-13"; income: "17" }
|
||||
ListElement{ timestamp: "2021-08"; expenses: "-5"; income: "9" }
|
||||
ListElement{ timestamp: "2021-09"; expenses: "-3"; income: "8" }
|
||||
ListElement{ timestamp: "2021-10"; expenses: "-13"; income: "15" }
|
||||
ListElement{ timestamp: "2021-11"; expenses: "-8"; income: "17" }
|
||||
ListElement{ timestamp: "2021-12"; expenses: "-7"; income: "10" }
|
||||
|
||||
ListElement{ timestamp: "2022-01"; expenses: "-12"; income: "16" }
|
||||
ListElement{ timestamp: "2022-02"; expenses: "-24"; income: "28" }
|
||||
ListElement{ timestamp: "2022-03"; expenses: "-27"; income: "22" }
|
||||
ListElement{ timestamp: "2022-04"; expenses: "-29"; income: "25" }
|
||||
ListElement{ timestamp: "2022-05"; expenses: "-27"; income: "29" }
|
||||
ListElement{ timestamp: "2022-06"; expenses: "-19"; income: "18" }
|
||||
ListElement{ timestamp: "2022-07"; expenses: "-13"; income: "17" }
|
||||
ListElement{ timestamp: "2022-08"; expenses: "-15"; income: "19" }
|
||||
ListElement{ timestamp: "2022-09"; expenses: "-3"; income: "8" }
|
||||
ListElement{ timestamp: "2022-10"; expenses: "-3"; income: "6" }
|
||||
ListElement{ timestamp: "2022-11"; expenses: "-4"; income: "8" }
|
||||
ListElement{ timestamp: "2022-12"; expenses: "-5"; income: "9" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtGraphs
|
||||
|
||||
Item {
|
||||
property real fontSize: 12
|
||||
property real windowRatio: 4
|
||||
property real maxAxisSegmentCount: 20
|
||||
property var graph: surfaceLayers
|
||||
|
||||
id: surfaceView
|
||||
|
||||
//! [0]
|
||||
Gradient {
|
||||
id: layerOneGradient
|
||||
GradientStop { position: 0.0; color: "black" }
|
||||
GradientStop { position: 0.31; color: "tan" }
|
||||
GradientStop { position: 0.32; color: "green" }
|
||||
GradientStop { position: 0.40; color: "darkslategray" }
|
||||
GradientStop { position: 1.0; color: "white" }
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: layerTwoGradient
|
||||
GradientStop { position: 0.315; color: "blue" }
|
||||
GradientStop { position: 0.33; color: "white" }
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: layerThreeGradient
|
||||
GradientStop { position: 0.0; color: "red" }
|
||||
GradientStop { position: 0.15; color: "black" }
|
||||
}
|
||||
//! [0]
|
||||
|
||||
Surface3D {
|
||||
id: surfaceLayers
|
||||
width: surfaceView.width
|
||||
height: surfaceView.height
|
||||
theme: GraphsTheme {
|
||||
theme: GraphsTheme.Theme.QtGreen
|
||||
labelFont.pointSize: 35
|
||||
colorStyle: GraphsTheme.ColorStyle.RangeGradient
|
||||
}
|
||||
shadowQuality: Graphs3D.ShadowQuality.None
|
||||
selectionMode: Graphs3D.SelectionFlag.Row | Graphs3D.SelectionFlag.Slice | Graphs3D.SelectionFlag.MultiSeries
|
||||
cameraPreset: Graphs3D.CameraPreset.IsometricLeft
|
||||
axisY.min: 20
|
||||
axisY.max: 200
|
||||
axisX.segmentCount: 5
|
||||
axisX.subSegmentCount: 2
|
||||
axisX.labelFormat: "%i"
|
||||
axisZ.segmentCount: 5
|
||||
axisZ.subSegmentCount: 2
|
||||
axisZ.labelFormat: "%i"
|
||||
axisY.segmentCount: 5
|
||||
axisY.subSegmentCount: 2
|
||||
axisY.labelFormat: "%i"
|
||||
|
||||
Surface3DSeries {
|
||||
id: layerOneSeries
|
||||
baseGradient: layerOneGradient
|
||||
HeightMapSurfaceDataProxy {
|
||||
heightMapFile: ":/heightmaps/layer_1.png"
|
||||
}
|
||||
shading: Surface3DSeries.Shading.Smooth
|
||||
drawMode: Surface3DSeries.DrawSurface
|
||||
}
|
||||
|
||||
Surface3DSeries {
|
||||
id: layerTwoSeries
|
||||
baseGradient: layerTwoGradient
|
||||
HeightMapSurfaceDataProxy {
|
||||
heightMapFile: ":/heightmaps/layer_2.png"
|
||||
}
|
||||
shading: Surface3DSeries.Shading.Smooth
|
||||
drawMode: Surface3DSeries.DrawSurface
|
||||
}
|
||||
|
||||
Surface3DSeries {
|
||||
id: layerThreeSeries
|
||||
baseGradient: layerThreeGradient
|
||||
HeightMapSurfaceDataProxy {
|
||||
heightMapFile: ":/heightmaps/layer_3.png"
|
||||
}
|
||||
shading: Surface3DSeries.Shading.Smooth
|
||||
drawMode: Surface3DSeries.DrawSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtGraphs
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: mainview
|
||||
width: 1920
|
||||
height: 1080
|
||||
|
||||
ColumnLayout {
|
||||
id: controls
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
property url selectedFile: fileDialog.selectedFile
|
||||
|
||||
Button {
|
||||
text: "Set Save Path"
|
||||
onClicked: {
|
||||
fileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Path to save :"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: controls.selectedFile.toString()
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
id: rowRadio
|
||||
text: "Row"
|
||||
checked: true
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: "Column"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Selected " + (rowRadio.checked ? "Row" : "Column")
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
validator: RegularExpressionValidator{
|
||||
regularExpression: /\d+/
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Slice To Image"
|
||||
onClicked: {
|
||||
var rowCol = (rowRadio.checked ? Graphs3D.SelectionFlag.Row : Graphs3D.SelectionFlag.Column)
|
||||
var index = textField.text
|
||||
if (tabBar.currentIndex === 0)
|
||||
surfaceGraph.graph.renderSliceToImage(-1, index, rowCol, controls.selectedFile);
|
||||
else
|
||||
barGraph.graph.renderSliceToImage(index, rowCol, controls.selectedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||
fileMode: FileDialog.SaveFile
|
||||
selectedFile: controls.selectedFile
|
||||
onAccepted: {
|
||||
controls.selectedFile = selectedFile
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: controls.left
|
||||
height: parent.height
|
||||
TabBar {
|
||||
id: tabBar
|
||||
width: parent.width
|
||||
TabButton {
|
||||
text: "Surface"
|
||||
}
|
||||
|
||||
TabButton {
|
||||
text: "Bar"
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
width: parent.width
|
||||
currentIndex: tabBar.currentIndex
|
||||
anchors.top: tabBar.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
SurfaceGraph {
|
||||
id: surfaceGraph
|
||||
}
|
||||
|
||||
BarGraph {
|
||||
id: barGraph
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
!include( ../tests.pri ) {
|
||||
error( "Couldn't find the tests.pri file!" )
|
||||
}
|
||||
|
||||
SOURCES += main.cpp
|
||||
|
||||
RESOURCES += qmlrenderslicetoimage.qrc
|
||||
|
||||
OTHER_FILES += qml/qmlrenderslicetoimage/*
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml/qmlgrabsliceview/main.qml</file>
|
||||
<file>qml/qmlgrabsliceview/SurfaceGraph.qml</file>
|
||||
<file>qml/qmlgrabsliceview/BarGraph.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/heightmaps">
|
||||
<file>layer_1.png</file>
|
||||
<file>layer_2.png</file>
|
||||
<file>layer_3.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Copyright (C) 2025 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||
project(tst_renderslicetoimage LANGUAGES C CXX ASM)
|
||||
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
|
||||
endif()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
qt_internal_add_manual_test(tst_renderslicetoimage
|
||||
GUI
|
||||
SOURCES
|
||||
bargraph.cpp bargraph.h
|
||||
bargraphmodifier.cpp bargraphmodifier.h
|
||||
bargraphwidget.cpp bargraphwidget.h
|
||||
main.cpp
|
||||
surfacegraph.cpp surfacegraph.h
|
||||
surfacegraphmodifier.cpp surfacegraphmodifier.h
|
||||
surfacegraphwidget.cpp surfacegraphwidget.h
|
||||
)
|
||||
|
||||
target_link_libraries(tst_renderslicetoimage PUBLIC
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
Qt::Widgets
|
||||
Qt::Graphs
|
||||
Qt::GraphsWidgets
|
||||
)
|
||||
|
||||
set(renderslicetoimage_resource_files
|
||||
)
|
||||
|
||||
qt6_add_resources(tst_renderslicetoimage "renderslicetoimage"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${renderslicetoimage_resource_files}
|
||||
)
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "bargraph.h"
|
||||
#include "bargraphmodifier.h"
|
||||
#include "bargraphwidget.h"
|
||||
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtGui/qvalidator.h>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
#include <QtWidgets/qboxlayout.h>
|
||||
#include <QtWidgets/qlabel.h>
|
||||
#include <QtWidgets/qlineedit.h>
|
||||
#include <QtWidgets/qpushbutton.h>
|
||||
#include <QtWidgets/qradiobutton.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
BarGraph::BarGraph(QWidget *parent)
|
||||
{
|
||||
m_barWidget = new QWidget(parent);
|
||||
initialize();
|
||||
}
|
||||
|
||||
void BarGraph::initialize()
|
||||
{
|
||||
m_barGraphWidget = new BarGraphWidget();
|
||||
m_barGraphWidget->initialize();
|
||||
QSize screenSize = m_barGraphWidget->screen()->size();
|
||||
m_barGraphWidget->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.75));
|
||||
m_barGraphWidget->setMaximumSize(screenSize);
|
||||
m_barGraphWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_barGraphWidget->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
QHBoxLayout *hLayout = new QHBoxLayout;
|
||||
hLayout->addWidget(m_barGraphWidget, 1);
|
||||
m_barWidget->setLayout(hLayout);
|
||||
|
||||
QVBoxLayout *vLayout = new QVBoxLayout;
|
||||
hLayout->addLayout(vLayout);
|
||||
vLayout->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_rowRadioButton = new QRadioButton(m_barWidget);
|
||||
m_rowRadioButton->setText(u"Row"_s);
|
||||
m_rowRadioButton->setChecked(true);
|
||||
|
||||
QRadioButton *columnRadioButton = new QRadioButton(m_barWidget);
|
||||
columnRadioButton->setText(u"Column"_s);
|
||||
columnRadioButton->setChecked(false);
|
||||
|
||||
m_lineSelectText = new QLineEdit(m_barWidget);
|
||||
QRegularExpression re("\\d+");
|
||||
QRegularExpressionValidator *reValidator = new QRegularExpressionValidator(re, m_barWidget);
|
||||
m_lineSelectText->setValidator(reValidator);
|
||||
|
||||
QPushButton *sliceToImageButton = new QPushButton(m_barWidget);
|
||||
sliceToImageButton->setText(u"Slice To Image"_s);
|
||||
|
||||
m_sliceResultLabel = new QLabel(m_barWidget);
|
||||
|
||||
vLayout->addWidget(m_rowRadioButton);
|
||||
vLayout->addWidget(columnRadioButton);
|
||||
vLayout->addWidget(m_lineSelectText);
|
||||
vLayout->addWidget(sliceToImageButton);
|
||||
vLayout->addWidget(m_sliceResultLabel);
|
||||
|
||||
m_barGraphWidget->raise();
|
||||
|
||||
m_modifier = new BarGraphModifier(m_barGraphWidget->barGraph(), this);
|
||||
|
||||
QObject::connect(sliceToImageButton,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&BarGraph::renderSliceToImage);
|
||||
}
|
||||
|
||||
void BarGraph::renderSliceToImage()
|
||||
{
|
||||
int index = m_lineSelectText->text().isEmpty() ? -1 : m_lineSelectText->text().toInt();
|
||||
QtGraphs3D::SliceType sliceType = QtGraphs3D::SliceType::SliceRow;
|
||||
if (!m_rowRadioButton->isChecked())
|
||||
sliceType = QtGraphs3D::SliceType::SliceColumn;
|
||||
|
||||
m_grab = m_modifier->renderSliceToImage(sliceType, index);
|
||||
connect(m_grab.data(), &QQuickItemGrabResult::ready, this, [&]() {
|
||||
m_sliceResultLabel->setPixmap(QPixmap::fromImage(m_grab.data()->image()));
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef BARGRAPH_H
|
||||
#define BARGRAPH_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
|
||||
class BarGraphModifier;
|
||||
class BarGraphWidget;
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
class QRadioButton;
|
||||
|
||||
class BarGraph : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BarGraph(QWidget *parent = nullptr);
|
||||
|
||||
void initialize();
|
||||
QWidget *barWidget() { return m_barWidget; }
|
||||
|
||||
private:
|
||||
void renderSliceToImage();
|
||||
|
||||
BarGraphModifier *m_modifier = nullptr;
|
||||
BarGraphWidget *m_barGraphWidget = nullptr;
|
||||
QWidget *m_barWidget = nullptr;
|
||||
|
||||
QRadioButton *m_rowRadioButton = nullptr;
|
||||
QLineEdit *m_lineSelectText = nullptr;
|
||||
QLabel *m_sliceResultLabel = nullptr;
|
||||
QSharedPointer<QQuickItemGrabResult> m_grab;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "bargraphmodifier.h"
|
||||
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QtGraphs/q3dscene.h>
|
||||
#include <QtGraphs/qgraphstheme.h>
|
||||
#include <QtGraphs/qbar3dseries.h>
|
||||
#include <QtGraphs/qbardataproxy.h>
|
||||
#include <QtGraphs/qcategory3daxis.h>
|
||||
#include <QtGraphs/qvalue3daxis.h>
|
||||
#include <QtWidgets/qcombobox.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
// TODO: Many of the values do not affect custom proxy series now - should be fixed
|
||||
|
||||
//! [set up the graph]
|
||||
BarGraphModifier::BarGraphModifier(Q3DBarsWidgetItem *bargraph, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_graph(bargraph)
|
||||
, m_temperatureAxis(new QValue3DAxis)
|
||||
, m_yearAxis(new QCategory3DAxis)
|
||||
, m_monthAxis(new QCategory3DAxis)
|
||||
, m_primarySeries(new QBar3DSeries)
|
||||
, m_secondarySeries(new QBar3DSeries)
|
||||
, m_celsiusString(u"°C"_s)
|
||||
{
|
||||
m_graph->setShadowQuality(QtGraphs3D::ShadowQuality::SoftMedium);
|
||||
m_graph->setMultiSeriesUniform(true);
|
||||
m_graph->activeTheme()->setPlotAreaBackgroundVisible(false);
|
||||
m_graph->activeTheme()->setLabelFont(QFont("Times New Roman", m_fontSize));
|
||||
m_graph->activeTheme()->setLabelBackgroundVisible(true);
|
||||
m_graph->setSelectionMode(QtGraphs3D::SelectionFlag::Column | QtGraphs3D::SelectionFlag::Slice);
|
||||
|
||||
m_months = {"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"};
|
||||
m_years = {"2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022"};
|
||||
|
||||
m_temperatureAxis->setTitle("Average temperature");
|
||||
m_temperatureAxis->setSegmentCount(m_segments);
|
||||
m_temperatureAxis->setSubSegmentCount(m_subSegments);
|
||||
m_temperatureAxis->setRange(m_minval, m_maxval);
|
||||
m_temperatureAxis->setLabelFormat(u"%.1f "_s + m_celsiusString);
|
||||
m_temperatureAxis->setLabelAutoAngle(30.0f);
|
||||
m_temperatureAxis->setTitleVisible(true);
|
||||
|
||||
m_yearAxis->setTitle("Year");
|
||||
m_yearAxis->setLabelAutoAngle(30.0f);
|
||||
m_yearAxis->setTitleVisible(true);
|
||||
|
||||
m_monthAxis->setTitle("Month");
|
||||
m_monthAxis->setLabelAutoAngle(30.0f);
|
||||
m_monthAxis->setTitleVisible(true);
|
||||
|
||||
m_graph->setValueAxis(m_temperatureAxis);
|
||||
m_graph->setRowAxis(m_yearAxis);
|
||||
m_graph->setColumnAxis(m_monthAxis);
|
||||
|
||||
m_primarySeries->setItemLabelFormat(u"Oulu - @colLabel @rowLabel: @valueLabel"_s);
|
||||
m_primarySeries->setMesh(QAbstract3DSeries::Mesh::BevelBar);
|
||||
m_primarySeries->setMeshSmooth(false);
|
||||
|
||||
m_secondarySeries->setItemLabelFormat(u"Helsinki - @colLabel @rowLabel: @valueLabel"_s);
|
||||
m_secondarySeries->setMesh(QAbstract3DSeries::Mesh::BevelBar);
|
||||
m_secondarySeries->setMeshSmooth(false);
|
||||
m_secondarySeries->setVisible(false);
|
||||
|
||||
m_graph->addSeries(m_primarySeries);
|
||||
m_graph->addSeries(m_secondarySeries);
|
||||
|
||||
changePresetCamera();
|
||||
|
||||
resetTemperatureData();
|
||||
}
|
||||
|
||||
void BarGraphModifier::resetTemperatureData()
|
||||
{
|
||||
static const float tempOulu[8][12] = {
|
||||
{-7.4f, -2.4f, 0.0f, 3.0f, 8.2f, 11.6f, 14.7f, 15.4f, 11.4f, 4.2f, 2.1f, -2.3f}, // 2015
|
||||
{-13.4f, -3.9f, -1.8f, 3.1f, 10.6f, 13.7f, 17.8f, 13.6f, 10.7f, 3.5f, -3.1f, -4.2f}, // 2016
|
||||
{-5.7f, -6.7f, -3.0f, -0.1f, 4.7f, 12.4f, 16.1f, 14.1f, 9.4f, 3.0f, -0.3f, -3.2f}, // 2017
|
||||
{-6.4f, -11.9f, -7.4f, 1.9f, 11.4f, 12.4f, 21.5f, 16.1f, 11.0f, 4.4f, 2.1f, -4.1f}, // 2018
|
||||
{-11.7f, -6.1f, -2.4f, 3.9f, 7.2f, 14.5f, 15.6f, 14.4f, 8.5f, 2.0f, -3.0f, -1.5f}, // 2019
|
||||
{-2.1f, -3.4f, -1.8f, 0.6f, 7.0f, 17.1f, 15.6f, 15.4f, 11.1f, 5.6f, 1.9f, -1.7f}, // 2020
|
||||
{-9.6f, -11.6f, -3.2f, 2.4f, 7.8f, 17.3f, 19.4f, 14.2f, 8.0f, 5.2f, -2.2f, -8.6f}, // 2021
|
||||
{-7.3f, -6.4f, -1.8f, 1.3f, 8.1f, 15.5f, 17.6f, 17.6f, 9.1f, 5.4f, -1.5f, -4.4f} // 2022
|
||||
};
|
||||
|
||||
static const float tempHelsinki[8][12] = {
|
||||
{-2.0f, -0.1f, 1.8f, 5.1f, 9.7f, 13.7f, 16.3f, 17.3f, 12.7f, 5.4f, 4.6f, 2.1f}, // 2015
|
||||
{-10.3f, -0.6f, 0.0f, 4.9f, 14.3f, 15.7f, 17.7f, 16.0f, 12.7f, 4.6f, -1.0f, -0.9f}, // 2016
|
||||
{-2.9f, -3.3f, 0.7f, 2.3f, 9.9f, 13.8f, 16.1f, 15.9f, 11.4f, 5.0f, 2.7f, 0.7f}, // 2017
|
||||
{-2.2f, -8.4f, -4.7f, 5.0f, 15.3f, 15.8f, 21.2f, 18.2f, 13.3f, 6.7f, 2.8f, -2.0f}, // 2018
|
||||
{-6.2f, -0.5f, -0.3f, 6.8f, 10.6f, 17.9f, 17.5f, 16.8f, 11.3f, 5.2f, 1.8f, 1.4f}, // 2019
|
||||
{1.9f, 0.5f, 1.7f, 4.5f, 9.5f, 18.4f, 16.5f, 16.8f, 13.0f, 8.2f, 4.4f, 0.9f}, // 2020
|
||||
{-4.7f, -8.1f, -0.9f, 4.5f, 10.4f, 19.2f, 20.9f, 15.4f, 9.5f, 8.0f, 1.5f, -6.7f}, // 2021
|
||||
{-3.3f, -2.2f, -0.2f, 3.3f, 9.6f, 16.9f, 18.1f, 18.9f, 9.2f, 7.6f, 2.3f, -3.4f} // 2022
|
||||
};
|
||||
|
||||
QBarDataArray dataSet;
|
||||
QBarDataArray dataSet2;
|
||||
|
||||
dataSet.reserve(m_years.size());
|
||||
for (qsizetype year = 0; year < m_years.size(); ++year) {
|
||||
QBarDataRow dataRow(m_months.size());
|
||||
QBarDataRow dataRow2(m_months.size());
|
||||
for (qsizetype month = 0; month < m_months.size(); ++month) {
|
||||
dataRow[month].setValue(tempOulu[year][month]);
|
||||
dataRow2[month].setValue(tempHelsinki[year][month]);
|
||||
}
|
||||
dataSet.append(dataRow);
|
||||
dataSet2.append(dataRow2);
|
||||
}
|
||||
|
||||
m_primarySeries->dataProxy()->resetArray(dataSet, m_years, m_months);
|
||||
m_secondarySeries->dataProxy()->resetArray(dataSet2, m_years, m_months);
|
||||
}
|
||||
|
||||
void BarGraphModifier::changePresetCamera()
|
||||
{
|
||||
m_graph->setCameraTargetPosition(QVector3D(0.0f, 0.0f, 0.0f));
|
||||
|
||||
static int preset = int(QtGraphs3D::CameraPreset::Front);
|
||||
|
||||
m_graph->setCameraPreset((QtGraphs3D::CameraPreset) preset);
|
||||
|
||||
if (++preset > int(QtGraphs3D::CameraPreset::DirectlyBelow))
|
||||
preset = int(QtGraphs3D::CameraPreset::FrontLow);
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult>
|
||||
BarGraphModifier::renderSliceToImage(QtGraphs3D::SliceType sliceType, int requestedIndex)
|
||||
{
|
||||
return m_graph->renderSliceToImage(requestedIndex, sliceType);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef BARGRAPHMODIFIER_H
|
||||
#define BARGRAPHMODIFIER_H
|
||||
|
||||
#include <QtCore/qpropertyanimation.h>
|
||||
#include <QtGraphs/qabstract3dseries.h>
|
||||
#include <QtGraphs/qbardataproxy.h>
|
||||
#include <QtGraphsWidgets/q3dbarswidgetitem.h>
|
||||
|
||||
class RainfallData;
|
||||
|
||||
class BarGraphModifier : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BarGraphModifier(Q3DBarsWidgetItem *bargraph, QObject *parent);
|
||||
virtual ~BarGraphModifier() = default;
|
||||
|
||||
void resetTemperatureData();
|
||||
void changePresetCamera();
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> renderSliceToImage(QtGraphs3D::SliceType sliceType,
|
||||
int requestedIndex);
|
||||
|
||||
private:
|
||||
Q3DBarsWidgetItem *m_graph = nullptr;
|
||||
int m_fontSize = 30;
|
||||
int m_segments = 4;
|
||||
int m_subSegments = 3;
|
||||
float m_minval = -20.f;
|
||||
float m_maxval = 20.f;
|
||||
QStringList m_months = {};
|
||||
QStringList m_years = {};
|
||||
QValue3DAxis *m_temperatureAxis = nullptr;
|
||||
QCategory3DAxis *m_yearAxis = nullptr;
|
||||
QCategory3DAxis *m_monthAxis = nullptr;
|
||||
QBar3DSeries *m_primarySeries = nullptr;
|
||||
QBar3DSeries *m_secondarySeries = nullptr;
|
||||
QAbstract3DSeries::Mesh m_barMesh = QAbstract3DSeries::Mesh::BevelBar;
|
||||
const QString m_celsiusString;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "bargraphwidget.h"
|
||||
|
||||
#include <QtGraphsWidgets/q3dbarswidgetitem.h>
|
||||
|
||||
BarGraphWidget::BarGraphWidget() {}
|
||||
|
||||
BarGraphWidget::~BarGraphWidget() {}
|
||||
|
||||
void BarGraphWidget::initialize()
|
||||
{
|
||||
m_barGraph = new Q3DBarsWidgetItem();
|
||||
m_barGraph->setWidget(this);
|
||||
}
|
||||
|
||||
Q3DBarsWidgetItem *BarGraphWidget::barGraph() const
|
||||
{
|
||||
return m_barGraph;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef BARGRAPHWIDGET_H
|
||||
#define BARGRAPHWIDGET_H
|
||||
|
||||
#include <QtQuickWidgets/qquickwidget.h>
|
||||
|
||||
class Q3DBarsWidgetItem;
|
||||
|
||||
class BarGraphWidget : public QQuickWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BarGraphWidget();
|
||||
~BarGraphWidget() override;
|
||||
|
||||
void initialize();
|
||||
Q3DBarsWidgetItem *barGraph() const;
|
||||
|
||||
private:
|
||||
Q3DBarsWidgetItem *m_barGraph = nullptr;
|
||||
};
|
||||
|
||||
#endif // BARGRAPHWIDGET_H
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "bargraph.h"
|
||||
#include "surfacegraph.h"
|
||||
|
||||
#include <QtWidgets/qapplication.h>
|
||||
#include <QtWidgets/qboxlayout.h>
|
||||
#include <QtWidgets/qstackedwidget.h>
|
||||
#include <QtWidgets/qcombobox.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QWidget *widget = new QWidget;
|
||||
|
||||
QStackedWidget *stackedWidget = new QStackedWidget;
|
||||
widget->setWindowTitle(u"Render slice to image example"_s);
|
||||
|
||||
SurfaceGraph surface;
|
||||
stackedWidget->addWidget(surface.surfaceWidget());
|
||||
|
||||
BarGraph bar;
|
||||
stackedWidget->addWidget(bar.barWidget());
|
||||
|
||||
QComboBox *comboBox = new QComboBox;
|
||||
comboBox->addItem(u"Surface"_s);
|
||||
comboBox->addItem(u"Bar"_s);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(comboBox, 1);
|
||||
layout->addWidget(stackedWidget);
|
||||
|
||||
QObject::connect(comboBox, &QComboBox::activated, stackedWidget, &QStackedWidget::setCurrentIndex);
|
||||
|
||||
widget->setLayout(layout);
|
||||
widget->show();
|
||||
int retVal = app.exec();
|
||||
return retVal;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
android|ios|winrt {
|
||||
error( "This example is not supported for android, ios, or winrt." )
|
||||
}
|
||||
|
||||
SOURCES += main.cpp surfacegraph.cpp surfacegraphmodifier.cpp surfacegraphwidget.cpp
|
||||
HEADERS += surfacegraph.h surfacegraphmodifier.h surfacegraphwidget.h
|
||||
|
||||
QT += widgets graphs
|
||||
|
||||
RESOURCES += renderslicetoimage.qrc
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "surfacegraph.h"
|
||||
#include "surfacegraphmodifier.h"
|
||||
#include "surfacegraphwidget.h"
|
||||
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtGui/qvalidator.h>
|
||||
#include <QtWidgets/qboxlayout.h>
|
||||
#include <QtWidgets/qlabel.h>
|
||||
#include <QtWidgets/qlineedit.h>
|
||||
#include <QtWidgets/qpushbutton.h>
|
||||
#include <QtWidgets/qradiobutton.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
SurfaceGraph::SurfaceGraph(QWidget *parent)
|
||||
{
|
||||
m_surfaceWidget = new QWidget(parent);
|
||||
initialize();
|
||||
}
|
||||
|
||||
void SurfaceGraph::initialize()
|
||||
{
|
||||
m_surfaceGraphWidget = new SurfaceGraphWidget();
|
||||
m_surfaceGraphWidget->initialize();
|
||||
QSize screenSize = m_surfaceGraphWidget->screen()->size();
|
||||
m_surfaceGraphWidget->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.75));
|
||||
m_surfaceGraphWidget->setMaximumSize(screenSize);
|
||||
m_surfaceGraphWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_surfaceGraphWidget->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
QHBoxLayout *hLayout = new QHBoxLayout;
|
||||
hLayout->addWidget(m_surfaceGraphWidget, 1);
|
||||
m_surfaceWidget->setLayout(hLayout);
|
||||
|
||||
QVBoxLayout *vLayout = new QVBoxLayout;
|
||||
hLayout->addLayout(vLayout);
|
||||
vLayout->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_rowRadioButton = new QRadioButton(m_surfaceWidget);
|
||||
m_rowRadioButton->setText(u"Row"_s);
|
||||
m_rowRadioButton->setChecked(true);
|
||||
|
||||
QRadioButton *columnRadioButton = new QRadioButton(m_surfaceWidget);
|
||||
columnRadioButton->setText(u"Column"_s);
|
||||
columnRadioButton->setChecked(false);
|
||||
|
||||
m_lineSelectText = new QLineEdit(m_surfaceWidget);
|
||||
QRegularExpression re("\\d+");
|
||||
QRegularExpressionValidator *reValidator = new QRegularExpressionValidator(re, m_surfaceWidget);
|
||||
m_lineSelectText->setValidator(reValidator);
|
||||
|
||||
QPushButton *sliceToImageButton = new QPushButton(m_surfaceWidget);
|
||||
sliceToImageButton->setText(u"Slice To Image"_s);
|
||||
|
||||
m_sliceResultLabel = new QLabel(m_surfaceWidget);
|
||||
|
||||
vLayout->addWidget(m_rowRadioButton);
|
||||
vLayout->addWidget(columnRadioButton);
|
||||
vLayout->addWidget(m_lineSelectText);
|
||||
vLayout->addWidget(sliceToImageButton);
|
||||
vLayout->addWidget(m_sliceResultLabel);
|
||||
|
||||
m_surfaceGraphWidget->raise();
|
||||
|
||||
m_modifier = new SurfaceGraphModifier(m_surfaceGraphWidget->surfaceGraph(), this);
|
||||
|
||||
QObject::connect(sliceToImageButton,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&SurfaceGraph::renderSliceToImage);
|
||||
}
|
||||
|
||||
void SurfaceGraph::renderSliceToImage()
|
||||
{
|
||||
int index = m_lineSelectText->text().isEmpty() ? -1 : m_lineSelectText->text().toInt();
|
||||
QtGraphs3D::SliceType sliceType = QtGraphs3D::SliceType::SliceRow;
|
||||
if (!m_rowRadioButton->isChecked())
|
||||
sliceType = QtGraphs3D::SliceType::SliceColumn;
|
||||
|
||||
m_grab = m_modifier->renderSliceToImage(sliceType, index);
|
||||
connect(m_grab.data(), &QQuickItemGrabResult::ready, this, [&]() {
|
||||
m_sliceResultLabel->setPixmap(QPixmap::fromImage(m_grab.data()->image()));
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef SURFACEGRAPH_H
|
||||
#define SURFACEGRAPH_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
class QRadioButton;
|
||||
class SurfaceGraphModifier;
|
||||
class SurfaceGraphWidget;
|
||||
|
||||
class SurfaceGraph : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SurfaceGraph(QWidget *parent = nullptr);
|
||||
|
||||
void initialize();
|
||||
QWidget *surfaceWidget() { return m_surfaceWidget; }
|
||||
|
||||
private:
|
||||
void renderSliceToImage();
|
||||
|
||||
SurfaceGraphModifier *m_modifier = nullptr;
|
||||
SurfaceGraphWidget *m_surfaceGraphWidget = nullptr;
|
||||
QWidget *m_surfaceWidget = nullptr;
|
||||
|
||||
QRadioButton *m_rowRadioButton = nullptr;
|
||||
QLineEdit *m_lineSelectText = nullptr;
|
||||
QLabel *m_sliceResultLabel = nullptr;
|
||||
QSharedPointer<QQuickItemGrabResult> m_grab;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "surfacegraphmodifier.h"
|
||||
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QtGraphs/qgraphstheme.h>
|
||||
#include <QtGraphs/qsurface3dseries.h>
|
||||
#include <QtGraphs/qsurfacedataproxy.h>
|
||||
#include <QtGraphsWidgets/q3dsurfacewidgetitem.h>
|
||||
#include <QtGraphs/qvalue3daxis.h>
|
||||
#include <QtGui/qimage.h>
|
||||
|
||||
SurfaceGraphModifier::SurfaceGraphModifier(Q3DSurfaceWidgetItem *surface, QObject *parent)
|
||||
: QObject(parent), m_graph(surface)
|
||||
{
|
||||
m_graph->setCameraZoomLevel(85.f);
|
||||
m_graph->setCameraPreset(QtGraphs3D::CameraPreset::IsometricRight);
|
||||
m_graph->activeTheme()->setTheme(QGraphsTheme::Theme::MixSeries);
|
||||
m_graph->activeTheme()->setLabelBackgroundVisible(false);
|
||||
m_graph->activeTheme()->setLabelBorderVisible(false);
|
||||
|
||||
m_graph->setAxisX(new QValue3DAxis);
|
||||
m_graph->setAxisY(new QValue3DAxis);
|
||||
m_graph->setAxisZ(new QValue3DAxis);
|
||||
|
||||
m_sqrtSinProxy = new QSurfaceDataProxy();
|
||||
m_sqrtSinSeries = new QSurface3DSeries(m_sqrtSinProxy);
|
||||
fillSqrtSinProxy();
|
||||
|
||||
m_sqrtSinSeries->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe);
|
||||
m_sqrtSinSeries->setShading(QSurface3DSeries::Shading::Flat);
|
||||
|
||||
m_graph->axisX()->setLabelFormat("%.2f");
|
||||
m_graph->axisZ()->setLabelFormat("%.2f");
|
||||
m_graph->axisX()->setRange(-8.f, 8.f);
|
||||
m_graph->axisY()->setRange(0.f, 2.f);
|
||||
m_graph->axisZ()->setRange(-8.f, 8.f);
|
||||
m_graph->axisX()->setLabelAutoAngle(30.f);
|
||||
m_graph->axisY()->setLabelAutoAngle(90.f);
|
||||
m_graph->axisZ()->setLabelAutoAngle(30.f);
|
||||
|
||||
m_graph->addSeries(m_sqrtSinSeries);
|
||||
|
||||
m_graph->setDefaultInputHandler();
|
||||
m_graph->setZoomEnabled(true);
|
||||
m_graph->setSelectionMode(QtGraphs3D::SelectionFlag::Row | QtGraphs3D::SelectionFlag::Slice);
|
||||
}
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult>
|
||||
SurfaceGraphModifier::renderSliceToImage(QtGraphs3D::SliceType sliceType, int requestedIndex)
|
||||
{
|
||||
return m_graph->renderSliceToImage(-1, requestedIndex, sliceType);
|
||||
}
|
||||
|
||||
SurfaceGraphModifier::~SurfaceGraphModifier() {}
|
||||
|
||||
void SurfaceGraphModifier::fillSqrtSinProxy()
|
||||
{
|
||||
float stepX = (8.f - -8.f) / float(150 - 1);
|
||||
float stepZ = (8.f - -8.f) / float(150 - 1);
|
||||
|
||||
QSurfaceDataArray dataArray;
|
||||
dataArray.reserve(150);
|
||||
for (int i = 0; i < 150; ++i) {
|
||||
QSurfaceDataRow newRow;
|
||||
newRow.reserve(150);
|
||||
float z = qMin(8.f, (i * stepZ + -8.f));
|
||||
for (int j = 0; j < 150; ++j) {
|
||||
float x = qMin(8.f, (j * stepX + -8.f));
|
||||
float R = qSqrt(z * z + x * x) + 0.01f;
|
||||
float y = (qSin(R) / R + 0.24f) * 1.61f;
|
||||
newRow.append(QSurfaceDataItem(x, y, z));
|
||||
}
|
||||
dataArray.append(newRow);
|
||||
}
|
||||
|
||||
m_sqrtSinProxy->resetArray(dataArray);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef SURFACEGRAPHMODIFIER_H
|
||||
#define SURFACEGRAPHMODIFIER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtGraphsWidgets/q3dsurfacewidgetitem.h>
|
||||
|
||||
class QSurfaceDataProxy;
|
||||
class QSurface3DSeries;
|
||||
class QQuickItemGrabResult;
|
||||
|
||||
class SurfaceGraphModifier : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SurfaceGraphModifier(Q3DSurfaceWidgetItem *surface, QObject *parent);
|
||||
~SurfaceGraphModifier();
|
||||
|
||||
QSharedPointer<QQuickItemGrabResult> renderSliceToImage(QtGraphs3D::SliceType sliceType,
|
||||
int requestedIndex);
|
||||
|
||||
private:
|
||||
void fillSqrtSinProxy();
|
||||
|
||||
private:
|
||||
Q3DSurfaceWidgetItem *m_graph = nullptr;
|
||||
QSurfaceDataProxy *m_sqrtSinProxy = nullptr;
|
||||
QSurface3DSeries *m_sqrtSinSeries = nullptr;
|
||||
};
|
||||
|
||||
#endif // SURFACEGRAPHMODIFIER_H
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "surfacegraphwidget.h"
|
||||
|
||||
#include <QtGraphsWidgets/q3dsurfacewidgetitem.h>
|
||||
|
||||
SurfaceGraphWidget::SurfaceGraphWidget() {}
|
||||
|
||||
SurfaceGraphWidget::~SurfaceGraphWidget() {}
|
||||
|
||||
void SurfaceGraphWidget::initialize()
|
||||
{
|
||||
m_surfaceGraph = new Q3DSurfaceWidgetItem();
|
||||
m_surfaceGraph->setWidget(this);
|
||||
}
|
||||
|
||||
Q3DSurfaceWidgetItem *SurfaceGraphWidget::surfaceGraph() const
|
||||
{
|
||||
return m_surfaceGraph;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef SURFACEGRAPHWIDGET_H
|
||||
#define SURFACEGRAPHWIDGET_H
|
||||
|
||||
#include <QtQuickWidgets/qquickwidget.h>
|
||||
|
||||
class Q3DSurfaceWidgetItem;
|
||||
|
||||
class SurfaceGraphWidget : public QQuickWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SurfaceGraphWidget();
|
||||
~SurfaceGraphWidget() override;
|
||||
|
||||
void initialize();
|
||||
Q3DSurfaceWidgetItem *surfaceGraph() const;
|
||||
|
||||
private:
|
||||
Q3DSurfaceWidgetItem *m_surfaceGraph = nullptr;
|
||||
};
|
||||
|
||||
#endif // SURFACEGRAPHWIDGET_H
|
||||
Loading…
Reference in New Issue