// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USE_STATIC_DATA class GraphDataGenerator : public QObject { public: explicit GraphDataGenerator(Q3DBarsWidgetItem *bargraph, QTableWidget *tableWidget); ~GraphDataGenerator(); void setupModel(); void addRow(); void changeStyle(); void changePresetCamera(); void changeTheme(); void start(); void selectFromTable(const QPoint &selection); void selectedFromTable(int currentRow, int currentColumn, int previousRow, int previousColumn); void fixTableSize(); private: Q3DBarsWidgetItem *m_graph; QTimer *m_dataTimer; int m_columnCount; int m_rowCount; QTableWidget *m_tableWidget; // not owned }; GraphDataGenerator::GraphDataGenerator(Q3DBarsWidgetItem *bargraph, QTableWidget *tableWidget) : m_graph(bargraph), m_dataTimer(0), m_columnCount(100), m_rowCount(50), m_tableWidget(tableWidget) { // Set up bar specifications; make the bars as wide as they are deep, // and add a small space between them m_graph->setBarThickness(1.0f); m_graph->setBarSpacing(QSizeF(0.2, 0.2)); #ifndef USE_STATIC_DATA // Set up sample space; make it as deep as it's wide m_graph->rowAxis()->setRange(0, m_rowCount); m_graph->columnAxis()->setRange(0, m_columnCount); m_tableWidget->setColumnCount(m_columnCount); // Set selection mode to full m_graph->setSelectionMode(QGraphs3DNamespace::SelectionFlag::ItemRowAndColumn); // Hide axis labels by explicitly setting one empty string as label list m_graph->rowAxis()->setLabels(QStringList(QString())); m_graph->columnAxis()->setLabels(QStringList(QString())); m_graph->seriesList().at(0)->setItemLabelFormat(QStringLiteral("@valueLabel")); #else // Set selection mode to slice row m_graph->setSelectionMode(QtGraphs3D::SelectionFlag::ItemAndRow | QtGraphs3D::SelectionFlag::Slice); #endif // Set theme m_graph->activeTheme()->setTheme(QGraphsTheme::Theme::QtGreen); // Set font QFont font = QFont("Impact", 20); font.setStyleHint(QFont::SansSerif); m_graph->activeTheme()->setLabelFont(font); // Set preset camera position m_graph->setCameraPreset(QtGraphs3D::CameraPreset::Front); } GraphDataGenerator::~GraphDataGenerator() { if (m_dataTimer) { m_dataTimer->stop(); delete m_dataTimer; } delete m_graph; } void GraphDataGenerator::start() { #ifndef USE_STATIC_DATA m_dataTimer = new QTimer(); m_dataTimer->setTimerType(Qt::CoarseTimer); QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::addRow); m_dataTimer->start(0); m_tableWidget->setFixedWidth(m_graph->width()); #else setupModel(); // Table needs to be shown before the size of its headers can be accurately obtained, // so we postpone it a bit m_dataTimer = new QTimer(); m_dataTimer->setSingleShot(true); QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::fixTableSize); m_dataTimer->start(0); #endif } void GraphDataGenerator::setupModel() { // Set up row and column names QStringList days; days << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday" << "Saturday" << "Sunday"; QStringList weeks; weeks << "week 1" << "week 2" << "week 3" << "week 4" << "week 5"; // Set up data Mon Tue Wed Thu Fri Sat Sun float hours[5][7] = {{2.0f, 1.0f, 3.0f, 0.2f, 1.0f, 5.0f, 10.0f}, // week 1 {0.5f, 1.0f, 3.0f, 1.0f, 2.0f, 2.0f, 3.0f}, // week 2 {1.0f, 1.0f, 2.0f, 1.0f, 4.0f, 4.0f, 4.0f}, // week 3 {0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, 0.3f}, // week 4 {3.0f, 3.0f, 6.0f, 2.0f, 2.0f, 1.0f, 1.0f}}; // week 5 // Add labels m_graph->rowAxis()->setTitle("Week of year"); m_graph->rowAxis()->setTitleVisible(true); m_graph->columnAxis()->setTitle("Day of week"); m_graph->columnAxis()->setTitleVisible(true); m_graph->valueAxis()->setTitle("Hours spent on the Internet"); m_graph->valueAxis()->setTitleVisible(true); m_graph->valueAxis()->setLabelFormat("%.1f h"); m_tableWidget->setRowCount(5); m_tableWidget->setColumnCount(7); m_tableWidget->setHorizontalHeaderLabels(days); m_tableWidget->setVerticalHeaderLabels(weeks); m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tableWidget->setCurrentCell(-1, -1); m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); for (int week = 0; week < weeks.size(); week++) { for (int day = 0; day < days.size(); day++) { QModelIndex index = m_tableWidget->model()->index(week, day); m_tableWidget->model()->setData(index, hours[week][day]); } } } void GraphDataGenerator::addRow() { m_tableWidget->model()->insertRow(0); if (m_tableWidget->model()->rowCount() > m_rowCount) m_tableWidget->model()->removeRow(m_rowCount); for (int i = 0; i < m_columnCount; i++) { QModelIndex index = m_tableWidget->model()->index(0, i); m_tableWidget->model()->setData(index, ((float)i / (float)m_columnCount) / 2.0f + (float)(QRandomGenerator::global()->bounded(30)) / 100.0f); } m_tableWidget->resizeColumnsToContents(); } void GraphDataGenerator::selectFromTable(const QPoint &selection) { m_tableWidget->setFocus(); m_tableWidget->setCurrentCell(selection.x(), selection.y()); } void GraphDataGenerator::selectedFromTable(int currentRow, int currentColumn, int previousRow, int previousColumn) { Q_UNUSED(previousRow); Q_UNUSED(previousColumn); m_graph->seriesList().at(0)->setSelectedBar(QPoint(currentRow, currentColumn)); } void GraphDataGenerator::fixTableSize() { int width = m_tableWidget->horizontalHeader()->length(); width += m_tableWidget->verticalHeader()->width(); m_tableWidget->setFixedWidth(width + 2); int height = m_tableWidget->verticalHeader()->length(); height += m_tableWidget->horizontalHeader()->height(); m_tableWidget->setFixedHeight(height + 2); } int main(int argc, char **argv) { QApplication app(argc, argv); Q3DBarsWidgetItem *graph = new Q3DBarsWidgetItem(); QWidget widget; QQuickWidget qqwidget; graph->setWidget(&qqwidget); QSize screenSize = graph->widget()->screen()->size(); graph->widget()->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 2)); graph->widget()->setMaximumSize(screenSize); graph->widget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); graph->widget()->setFocusPolicy(Qt::StrongFocus); QVBoxLayout *layout = new QVBoxLayout(&widget); QTableWidget *tableWidget = new QTableWidget(&widget); layout->addWidget(graph->widget(), 1); layout->addWidget(tableWidget, 1, Qt::AlignHCenter); tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); tableWidget->setAlternatingRowColors(true); widget.setWindowTitle(QStringLiteral("Hours spent on the Internet")); // Since we are dealing with QTableWidget, the model will already have data sorted properly // into rows and columns, so we simply set useModelCategories property to true to utilize this. QItemModelBarDataProxy *proxy = new QItemModelBarDataProxy(tableWidget->model()); proxy->setUseModelCategories(true); QBar3DSeries *series = new QBar3DSeries(proxy); series->setMesh(QAbstract3DSeries::Mesh::Pyramid); graph->addSeries(series); GraphDataGenerator generator(graph, tableWidget); QObject::connect(series, &QBar3DSeries::selectedBarChanged, &generator, &GraphDataGenerator::selectFromTable); QObject::connect(tableWidget, &QTableWidget::currentCellChanged, &generator, &GraphDataGenerator::selectedFromTable); widget.show(); generator.start(); return app.exec(); }