pure-cpp 1.0.0
A C++ physics simulation benchmark comparing performance with Python implementations
qml_bridge.cpp
Go to the documentation of this file.
1/**
2 * \file qml_bridge.cpp
3 * \brief Implementation of the QML bridge.
4 * \author Le Bars, Yoann
5 *
6 * This file is part of the pure C++ benchmark.
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation, either version 3 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include "qml_bridge.hpp"
23
24#include <QApplication>
25#include <QFile>
26#include <QMessageBox>
27#include <QTextStream>
28#include <QWidget>
29#include <config.hpp>
30
31namespace Window {
32
33 QmlBridge::QmlBridge(const Configuration::SimulationConfig& config,
34 QObject* parent)
35 : QObject(parent), container_(nullptr) {
36 // Create the 3D display
37 display_ = std::make_unique<Display>(config);
38
39 // Create a container widget for the Qt3D window
40 // IMPORTANT: createWindowContainer does NOT take ownership of the window
41 // The Display must remain alive as long as the container exists
42 container_ = QWidget::createWindowContainer(display_.get());
43 container_->setFocusPolicy(Qt::TabFocus);
44 // Set parent to ensure proper cleanup
45 container_->setParent(nullptr); // Will be reparented when added to layout
46
47 // Connect the simulation finished signal
48 connect(display_.get(), &Display::simulationFinished, this,
50
51 // Load about text
52 const auto aboutTextTemplate = loadAboutText();
53 if (aboutTextTemplate) {
54 aboutText_ = *aboutTextTemplate;
55 aboutText_.replace(QStringLiteral("{}"),
56 QApplication::applicationVersion());
57 } else {
58 aboutText_ = tr("Could not load the About page. The resource "
59 "file might be missing or corrupted.");
60 }
61
63 emit aboutTextChanged();
64 }
65
67 // Let Qt handle widget destruction automatically.
68 // The container_ is owned by its parent widget (mainWindow), so it will
69 // be destroyed when the parent is destroyed by QApplication.
70 // The display_ will be destroyed by unique_ptr automatically.
71 }
72
73 QWidget* QmlBridge::displayContainer() const { return container_; }
74
75 QString QmlBridge::aboutText() const { return aboutText_; }
76
78 if (display_) {
79 display_->runSimulation();
80 }
81 }
82
84 // Cleanup the display to stop threads and print profiling reports.
85 // Let QApplication handle widget destruction automatically.
86 if (display_) {
87 display_->cleanup();
88 }
89 }
90
92 // Manually destroy the Display before the container is destroyed.
93 // This prevents segfaults that occur when the container tries to access
94 // the Display during its destruction.
95 if (display_) {
96 display_.reset();
97 }
98 }
99
100 void QmlBridge::releaseDisplayDeferred(QWidget* container_for_deferred_delete) {
101 // Defer Display destruction to avoid blocking cleanup on heavy Qt3D
102 // teardown. The ownership is released from unique_ptr and transferred
103 // to Qt's deferred deletion mechanism.
104 if (display_) {
105 Display* displayRaw = display_.release();
106 if (container_for_deferred_delete) {
107 // Enforce safe order: container is deleted only after Display.
108 QObject::connect(displayRaw, &QObject::destroyed,
109 container_for_deferred_delete,
110 &QObject::deleteLater, Qt::QueuedConnection);
111 }
112 displayRaw->deleteLater();
113 } else if (container_for_deferred_delete) {
114 // Fallback: if Display is already gone, schedule container directly.
115 container_for_deferred_delete->deleteLater();
116 }
117 }
118
120 QMessageBox::about(
121 nullptr, tr("About Pure C++", "About message box title"),
122 aboutText_);
123 }
124
126 QApplication::aboutQt();
127 }
128
129 void QmlBridge::quit() { QApplication::quit(); }
130
131 std::optional<QString> QmlBridge::loadAboutText() {
132 // It tries to load a locale-specific version first (e.g.,
133 // `about_fr_FR.htm`), falling back to the default `about.htm` if
134 // the specific version is not found.
135 const QStringList pathsToTry = {
136 QStringLiteral(":/locales/about_%1.htm")
137 .arg(QLocale::system().name()),
138 QStringLiteral(":/locales/about.htm")};
139
140 for (const auto& path : pathsToTry) {
141 QFile file(path);
142 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
143 QTextStream in(&file);
144 return in.readAll();
145 }
146 }
147
148 return std::nullopt; // Return empty if no file could be opened.
149 }
150
151} // namespace Window
152
Manages the Qt3D window, scene setup, and the main simulation loop.
Definition: display.hpp:52
void simulationFinished()
Emitted when the simulation has run for n_iter iterations.
QmlBridge(const Configuration::SimulationConfig &config, QObject *parent=nullptr)
Constructs the QML bridge.
Definition: qml_bridge.cpp:33
Q_INVOKABLE void startSimulation()
Starts the simulation after the window is shown.
Definition: qml_bridge.cpp:77
std::optional< QString > loadAboutText()
Loads the "About" page content from resources.
Definition: qml_bridge.cpp:131
QString aboutText_
The cached about text.
Definition: qml_bridge.hpp:149
QWidget * container_
The widget container for the 3D display.
Definition: qml_bridge.hpp:146
void aboutTextChanged()
Emitted when the about text changes.
std::unique_ptr< Display > display_
The 3D display view for the simulation.
Definition: qml_bridge.hpp:143
void displayContainerChanged()
Emitted when the display container is created.
Q_INVOKABLE void cleanup()
Triggers cleanup of child components (stops threads, prints reports).
Definition: qml_bridge.cpp:83
void showAboutQt()
Slot to display the "About Qt" dialogue.
Definition: qml_bridge.cpp:125
void releaseDisplayDeferred(QWidget *container_for_deferred_delete)
Releases the Display using deferred destruction.
Definition: qml_bridge.cpp:100
~QmlBridge() override
Destructor.
Definition: qml_bridge.cpp:66
void quit()
Slot to quit the application.
Definition: qml_bridge.cpp:129
void simulationFinished()
Emitted when the simulation finishes.
void showAbout()
Slot to display the "About" dialogue.
Definition: qml_bridge.cpp:119
void releaseDisplay()
Releases the Display (destroys it manually). This must be called before the container is destroyed to...
Definition: qml_bridge.cpp:91
Bridge class to expose C++ functionality to QML.