mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 18:57:17 +00:00
Add IFC to RDB conversion in IfcViewerFull
Wire the AddModelDialog "Convert IFC File to Database" button to a new ConvertToDatabase source mode handled by ModelsPanelController, which prompts for an .ifc input and .rdb output then runs the existing document_serializer_registry "rdb" plugin on a background QThread with a modal progress dialog. Build the src/serializers subdir for BUILD_IFCVIEWER so the rdb plugin is produced, and align serializer plugin runtime output with the kernel/mapping plugins by writing them into $<TARGET_FILE_DIR:IfcGeom> so default plugin discovery finds them in both dev and install layouts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -547,9 +547,9 @@ if(BUILD_IFCGEOM)
|
|||||||
add_subdirectory(../src/ifcgeom ifcgeom)
|
add_subdirectory(../src/ifcgeom ifcgeom)
|
||||||
endif(BUILD_IFCGEOM)
|
endif(BUILD_IFCGEOM)
|
||||||
|
|
||||||
if(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
if(BUILD_CONVERT OR BUILD_IFCPYTHON OR BUILD_IFCVIEWER)
|
||||||
add_subdirectory(../src/serializers serializers)
|
add_subdirectory(../src/serializers serializers)
|
||||||
endif(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
endif(BUILD_CONVERT OR BUILD_IFCPYTHON OR BUILD_IFCVIEWER)
|
||||||
|
|
||||||
if(BUILD_CONVERT)
|
if(BUILD_CONVERT)
|
||||||
add_subdirectory(../src/ifcconvert ifcconvert)
|
add_subdirectory(../src/ifcconvert ifcconvert)
|
||||||
|
|||||||
@@ -120,8 +120,9 @@ void AddModelDialog::setupUi() {
|
|||||||
default_description));
|
default_description));
|
||||||
|
|
||||||
auto* convert_database = components::buttons::makeButton("Convert IFC File\nto Database", ":/icons/database-restore.svg", choices);
|
auto* convert_database = components::buttons::makeButton("Convert IFC File\nto Database", ":/icons/database-restore.svg", choices);
|
||||||
connect(convert_database, &QToolButton::clicked, this, [description]() {
|
connect(convert_database, &QToolButton::clicked, this, [this]() {
|
||||||
description->setText("IFC-to-database conversion is coming soon.");
|
selected_mode_ = SourceMode::ConvertToDatabase;
|
||||||
|
accept();
|
||||||
});
|
});
|
||||||
convert_database->installEventFilter(new HoverDescriptionFilter(
|
convert_database->installEventFilter(new HoverDescriptionFilter(
|
||||||
description,
|
description,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ enum class SourceMode {
|
|||||||
IfcFile,
|
IfcFile,
|
||||||
IfcDatabase,
|
IfcDatabase,
|
||||||
GeometryOnly,
|
GeometryOnly,
|
||||||
|
ConvertToDatabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddModelDialog : public components::Dialog {
|
class AddModelDialog : public components::Dialog {
|
||||||
|
|||||||
@@ -31,16 +31,23 @@
|
|||||||
#include "../../../ifcviewer/SceneLoader.h"
|
#include "../../../ifcviewer/SceneLoader.h"
|
||||||
#include "../../../ifcviewer/SidecarCache.h"
|
#include "../../../ifcviewer/SidecarCache.h"
|
||||||
#include "../../../ifcviewer/ViewportWindow.h"
|
#include "../../../ifcviewer/ViewportWindow.h"
|
||||||
|
#include "../../../ifcgeom/Serializer.h"
|
||||||
|
#include "../../../serializers/document_serializer_plugin.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QDebug>
|
#include <QFileInfo>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
#include <QThread>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace ifcviewerfull::modules::models {
|
namespace ifcviewerfull::modules::models {
|
||||||
|
|
||||||
ModelsPanelController::ModelsPanelController(QWidget* host,
|
ModelsPanelController::ModelsPanelController(QWidget* host,
|
||||||
@@ -195,6 +202,9 @@ void ModelsPanelController::addFiles() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case modules::models::SourceMode::ConvertToDatabase:
|
||||||
|
convertIfcToDatabase();
|
||||||
|
return;
|
||||||
case modules::models::SourceMode::None:
|
case modules::models::SourceMode::None:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -202,6 +212,123 @@ void ModelsPanelController::addFiles() {
|
|||||||
addFiles(paths);
|
addFiles(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelsPanelController::convertIfcToDatabase() {
|
||||||
|
QFileDialog input_dialog(host_, "Select IFC File to Convert");
|
||||||
|
input_dialog.setFileMode(QFileDialog::ExistingFile);
|
||||||
|
input_dialog.setNameFilter("IFC Files (*.ifc);;All Files (*)");
|
||||||
|
input_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
|
||||||
|
if (input_dialog.exec() != QDialog::Accepted) return;
|
||||||
|
const QStringList inputs = input_dialog.selectedFiles();
|
||||||
|
if (inputs.isEmpty()) return;
|
||||||
|
const QString input_path = inputs.first();
|
||||||
|
|
||||||
|
const QFileInfo input_info(input_path);
|
||||||
|
const QString default_output = input_info.absoluteDir().filePath(input_info.completeBaseName() + ".rdb");
|
||||||
|
|
||||||
|
QFileDialog output_dialog(host_, "Save IFC Database As");
|
||||||
|
output_dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
|
output_dialog.setFileMode(QFileDialog::AnyFile);
|
||||||
|
output_dialog.setNameFilter("IFC Database (*.rdb);;All Files (*)");
|
||||||
|
output_dialog.setDefaultSuffix("rdb");
|
||||||
|
output_dialog.selectFile(default_output);
|
||||||
|
output_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
|
||||||
|
output_dialog.setOption(QFileDialog::DontConfirmOverwrite, true);
|
||||||
|
if (output_dialog.exec() != QDialog::Accepted) return;
|
||||||
|
const QStringList outputs = output_dialog.selectedFiles();
|
||||||
|
if (outputs.isEmpty()) return;
|
||||||
|
QString output_path = outputs.first();
|
||||||
|
if (!output_path.endsWith(".rdb", Qt::CaseInsensitive)) {
|
||||||
|
output_path += ".rdb";
|
||||||
|
}
|
||||||
|
|
||||||
|
const QFileInfo output_info(output_path);
|
||||||
|
if (output_info.exists()) {
|
||||||
|
const QString message = QString("'%1' already exists. Overwrite?").arg(output_info.fileName());
|
||||||
|
if (QMessageBox::question(host_, "Convert IFC to Database", message,
|
||||||
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runIfcToDatabaseConversion(input_path, output_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelsPanelController::runIfcToDatabaseConversion(const QString& input_path, const QString& output_path) {
|
||||||
|
auto* progress = new QProgressDialog(host_);
|
||||||
|
progress->setWindowTitle("Convert IFC to Database");
|
||||||
|
progress->setLabelText(QString("Converting %1 to %2…")
|
||||||
|
.arg(QFileInfo(input_path).fileName(),
|
||||||
|
QFileInfo(output_path).fileName()));
|
||||||
|
progress->setRange(0, 0);
|
||||||
|
progress->setCancelButton(nullptr);
|
||||||
|
progress->setMinimumDuration(0);
|
||||||
|
progress->setWindowModality(Qt::ApplicationModal);
|
||||||
|
progress->setAutoClose(false);
|
||||||
|
progress->setAutoReset(false);
|
||||||
|
progress->show();
|
||||||
|
|
||||||
|
session_state_->setStatusMessage("Converting",
|
||||||
|
QString("%1 → %2").arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName()));
|
||||||
|
|
||||||
|
auto timer = std::make_shared<QElapsedTimer>();
|
||||||
|
timer->start();
|
||||||
|
auto error_message = std::make_shared<QString>();
|
||||||
|
|
||||||
|
QThread* thread = QThread::create([input_path, output_path, error_message]() {
|
||||||
|
try {
|
||||||
|
ifcopenshell::serializers::document_serializer_context context;
|
||||||
|
context.file = nullptr;
|
||||||
|
context.input_filename = input_path.toStdString();
|
||||||
|
context.output_filename = output_path.toStdString();
|
||||||
|
context.stream = true;
|
||||||
|
|
||||||
|
auto& registry = ifcopenshell::serializers::document_serializer_registry_instance();
|
||||||
|
const auto* info = registry.find("rdb");
|
||||||
|
if (!info) {
|
||||||
|
throw ifcopenshell::exception(
|
||||||
|
"No 'rdb' document serializer is registered. The RocksDB serializer plugin may not be installed.");
|
||||||
|
}
|
||||||
|
if (!info->supports_input_filename) {
|
||||||
|
throw ifcopenshell::exception("RDB serializer does not support streaming from an input filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<Serializer> serializer = registry.create("rdb", context);
|
||||||
|
serializer->finalize();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
*error_message = QString::fromUtf8(e.what());
|
||||||
|
} catch (...) {
|
||||||
|
*error_message = "Unknown error during IFC to RDB conversion";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(thread, &QThread::finished, this,
|
||||||
|
[this, thread, progress, timer, error_message, input_path, output_path]() {
|
||||||
|
const qint64 elapsed = timer->elapsed();
|
||||||
|
|
||||||
|
progress->close();
|
||||||
|
progress->deleteLater();
|
||||||
|
thread->deleteLater();
|
||||||
|
|
||||||
|
if (!error_message->isEmpty()) {
|
||||||
|
session_state_->setStatusMessage("Error", *error_message);
|
||||||
|
QMessageBox::warning(host_, "Convert IFC to Database",
|
||||||
|
QString("Conversion failed:\n%1").arg(*error_message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_state_->setStatusMessage(
|
||||||
|
"Converted",
|
||||||
|
QString("%1 → %2 in %3")
|
||||||
|
.arg(QFileInfo(input_path).fileName(),
|
||||||
|
QFileInfo(output_path).fileName(),
|
||||||
|
formatElapsed(elapsed)));
|
||||||
|
QMessageBox::information(host_, "Convert IFC to Database",
|
||||||
|
QString("Database written to:\n%1").arg(output_path));
|
||||||
|
});
|
||||||
|
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
void ModelsPanelController::addFiles(const QStringList& paths) {
|
void ModelsPanelController::addFiles(const QStringList& paths) {
|
||||||
QStringList accepted_paths;
|
QStringList accepted_paths;
|
||||||
QStringList accepted_fed_ids;
|
QStringList accepted_fed_ids;
|
||||||
|
|||||||
@@ -51,10 +51,12 @@ public:
|
|||||||
void loadModels(const QStringList& paths, const QStringList& fed_ids);
|
void loadModels(const QStringList& paths, const QStringList& fed_ids);
|
||||||
void removeLoadedModel(const QString& fed_id);
|
void removeLoadedModel(const QString& fed_id);
|
||||||
void openSettings();
|
void openSettings();
|
||||||
|
void convertIfcToDatabase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString formatElapsed(qint64 ms) const;
|
QString formatElapsed(qint64 ms) const;
|
||||||
void writeSidecarForModel(SceneLoader* loader, uint32_t mid) const;
|
void writeSidecarForModel(SceneLoader* loader, uint32_t mid) const;
|
||||||
|
void runIfcToDatabaseConversion(const QString& input_path, const QString& output_path);
|
||||||
|
|
||||||
QWidget* host_ = nullptr;
|
QWidget* host_ = nullptr;
|
||||||
ModelsPanel* widget_ = nullptr;
|
ModelsPanel* widget_ = nullptr;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
set(document_serializer_plugin_runtime_dir "${CMAKE_BINARY_DIR}/serializers/$<CONFIG>")
|
set(document_serializer_plugin_runtime_dir "$<TARGET_FILE_DIR:IfcGeom>")
|
||||||
set(geometry_serializer_plugin_runtime_dir "${CMAKE_BINARY_DIR}/serializers/$<CONFIG>")
|
set(geometry_serializer_plugin_runtime_dir "$<TARGET_FILE_DIR:IfcGeom>")
|
||||||
|
|
||||||
file(GLOB SERIALIZERS_H_FILES *.h)
|
file(GLOB SERIALIZERS_H_FILES *.h)
|
||||||
file(GLOB SERIALIZERS_S_H_FILES schema_dependent/*.h)
|
file(GLOB SERIALIZERS_S_H_FILES schema_dependent/*.h)
|
||||||
|
|||||||
Reference in New Issue
Block a user