diff --git a/flake.nix b/flake.nix index 35ab78da..120d6fec 100644 --- a/flake.nix +++ b/flake.nix @@ -238,6 +238,32 @@ nativeBuildInputs = old.nativeBuildInputs ++ [ prev.libsForQt5.wrapQtAppsHook ]; + prePatch = let + icon = lib.nix-filter { + root = ./.; + include = [ + "icon" + ]; + }; + in '' + # Allow to open .csx files + substituteInPlace QCSXCAD.cpp --replace-fail 'XML-File (*.xml)' 'XML-File (*.xml *.csx)' + + # Open OEMSH + cp ${icon}/icon/openemsh.ico images/openemsh.ico + sed -i resources.qrc \ + -e '/^ images\/QCSXCAD_Icon.png<\/file>/a\ images\/openemsh.ico<\/file>' + sed -i QCSGridEditor.h \ + -e '/^ void DetectEdges();/a\ void RunOpenEMSH();' \ + -e '/^ void signalDetectEdges(int);/a\ void signalRunOpenEMSH();' + sed -i QCSGridEditor.cpp \ + -e '/^ TB->addAction(tr("detect \\nedges"),this,SLOT(DetectEdges()));/a\ TB->addAction(QPixmap(":\/images\/openemsh.ico"),tr("Run OpenEMSH"),this,SLOT(RunOpenEMSH()));' \ + -e '/^void QCSGridEditor::BuildHomogenDisc()/i\void QCSGridEditor::RunOpenEMSH() { emit signalRunOpenEMSH(); }' + sed -i QCSXCAD.h \ + -e '/^ virtual void clear();/i\ virtual void RunOpenEMSH() {}' + sed -i QCSXCAD.cpp \ + -e '/^ QObject::connect(GridEditor,SIGNAL(signalDetectEdges(int)),this,SLOT(DetectEdges(int)));/a\ QObject::connect(GridEditor,SIGNAL(signalRunOpenEMSH()),this,SLOT(RunOpenEMSH()));' + ''; })).override { mkDerivation = prev.fastStdenv.mkDerivation; inherit (final) csxcad; @@ -247,6 +273,49 @@ nativeBuildInputs = old.nativeBuildInputs ++ [ prev.libsForQt5.wrapQtAppsHook ]; + prePatch = let + run_oemsh = '' + void AppCSXCAD::RunOpenEMSH() + { + if(bModified) { + if(QMessageBox::question(this, tr("OpenEMSH mesher"), tr("Save current Geometry before meshing? (only geometry matters, mesh will be overwritten)"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) { + Save(); + bModified=false; + } + } + + setEnabled(false); + repaint(); + QGuiApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor)); + int ret = QProcess::execute("openemsh", { "-Gvf", "--input", m_filename }); + QGuiApplication::restoreOverrideCursor(); + setEnabled(true); + + if(ret == -1) { + QMessageBox::warning(this, tr("OpenEMSH mesher"), tr("Error OpenEMSH crashed!")); + } else if(ret == -2) { + QMessageBox::warning(this, tr("OpenEMSH mesher"), tr("Error OpenEMSH could not start!")); + } else { + ReadFile(m_filename); + } + } + ''; + in '' + # Set icon + # TODO add Windows RC file + sed -i AppCSXCAD.cpp \ + -e '/^ QString title = tr("AppCSXCAD");/a\ setWindowIcon(QPixmap(":/images/QCSXCAD_Icon.png"));' + + # Open OEMSH + sed -i AppCSXCAD.h \ + -e '/^ virtual void clear();/a\void RunOpenEMSH() override;' + sed -i AppCSXCAD.cpp \ + -e '/^QMenu *InfoMenu = mb->addMenu("Info");/a\ QObject::connect(this, QCSXCAD::signalRunOpenEMSH, this, AppCSXCAD::RunOpenEMSH);' \ + -e 's/ return QCSXCAD::ReadFile(filename);/ bool ret = QCSXCAD::ReadFile(filename);\n bModified = false;\n return ret;/' + cat >> AppCSXCAD.cpp << EOF + ${run_oemsh} + EOF + ''; })).override { mkDerivation = prev.fastStdenv.mkDerivation; inherit (final) csxcad qcsxcad; diff --git a/icon/qcsxcad.png b/icon/qcsxcad.png new file mode 100644 index 00000000..a1530be5 Binary files /dev/null and b/icon/qcsxcad.png differ diff --git a/src/infra/parsers/parser_from_csx.cpp b/src/infra/parsers/parser_from_csx.cpp index 48ddb95a..8099fb2d 100644 --- a/src/infra/parsers/parser_from_csx.cpp +++ b/src/infra/parsers/parser_from_csx.cpp @@ -673,12 +673,24 @@ expected ParserFromCsx::parse() { TRY(pimpl->parse_oemsh(oemsh.node())); } - if(!doc.select_node("/openEMS")) + bool is_under_openems; + if(doc.select_node("/openEMS/ContinuousStructure")) + is_under_openems = true; + else if(doc.select_node("/ContinuousStructure")) + is_under_openems = false; + else return unexpected("No \"/openEMS\" path in CSX XML file"); - pugi::xpath_node fdtd = doc.select_node("/openEMS/FDTD"); + auto const root = [&](string const str) { + if(is_under_openems) + return "/openEMS"s + str; + else + return str; + }; + + pugi::xpath_node fdtd = doc.select_node(root("/FDTD").c_str()); - pugi::xpath_node csx = doc.select_node("/openEMS/ContinuousStructure"); + pugi::xpath_node csx = doc.select_node(root("/ContinuousStructure").c_str()); TRY(pimpl->parse_grid(csx.node())); pimpl->board.set_background_material(pimpl->parse_property(csx.node().child("BackgroundMaterial"))); @@ -686,7 +698,7 @@ expected ParserFromCsx::parse() { { // Primitives' IDs grow disregarding properties. size_t id = 0; - pugi::xpath_node_set primitives = doc.select_nodes("/openEMS/ContinuousStructure/Properties/*/Primitives"); + pugi::xpath_node_set primitives = doc.select_nodes(root("/ContinuousStructure/Properties/*/Primitives").c_str()); for(auto const& primitive : primitives) for(auto const& node : primitive.node().children()) { pimpl->primitives_ids.emplace(node, id++); @@ -698,7 +710,7 @@ expected ParserFromCsx::parse() { pimpl->primitives_ids.size(), "Parsing primitives "); - pugi::xpath_node properties = doc.select_node("/openEMS/ContinuousStructure/Properties"); + pugi::xpath_node properties = doc.select_node(root("/ContinuousStructure/Properties").c_str()); for(auto const& node : properties.node().children()) { auto material = pimpl->parse_property(node); diff --git a/src/infra/serializers/serializer_to_csx.cpp b/src/infra/serializers/serializer_to_csx.cpp index 456901df..aee005f8 100644 --- a/src/infra/serializers/serializer_to_csx.cpp +++ b/src/infra/serializers/serializer_to_csx.cpp @@ -82,8 +82,13 @@ void SerializerToCsx::visit(Board& board) { return; } - pugi::xml_node oems = find_or_append_child(doc, "openEMS"); - pugi::xml_node csx = find_or_append_child(oems, "ContinuousStructure"); + pugi::xml_node csx; + if(doc.select_node("/ContinuousStructure")) { + csx = find_or_append_child(doc, "ContinuousStructure"); + } else { + pugi::xml_node oems = find_or_append_child(doc, "openEMS"); + csx = find_or_append_child(oems, "ContinuousStructure"); + } pugi::xml_node grid = find_or_append_child(csx, "RectilinearGrid"); grid.remove_children(); diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 7fe2ff0e..108a7adf 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -35,6 +37,21 @@ namespace ui::qt { using DisplayMode = ProcessingScene::DisplayMode; using MeshVisibility = StructureScene::MeshVisibility; +//****************************************************************************** +static QString get_shortcuts(QAction const* p) { return QKeySequence::listToString(p->shortcuts()); } +static QString get_shortcuts(QToolButton const* p) { return p->shortcut().toString(); } + +//****************************************************************************** +static void add_shortcut_to_tooltip(auto* p, QString const& shortcut = QString()) { + QString s = shortcut.isNull() + ? get_shortcuts(p) + : shortcut; + if(!s.isEmpty()) + p->setToolTip(QString("%1 (%2)") + .arg(p->toolTip()) + .arg(s)); +} + //****************************************************************************** MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) : QMainWindow(parent) @@ -50,8 +67,34 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) // TODO Init StructureView & ProcessingView stuff from buttons default values + update_board_dependant_buttons_visibility(false); + ui->statusBar->addPermanentWidget(ui->l_cell_number); + ui->a_edit->setShortcuts(ui->a_edit->shortcuts() += QKeySequence(Qt::Key_Space)); + add_shortcut_to_tooltip(ui->a_edit); + add_shortcut_to_tooltip(ui->a_fit); + add_shortcut_to_tooltip(ui->a_appcsxcad); + add_shortcut_to_tooltip(ui->a_file_open); + add_shortcut_to_tooltip(ui->a_file_save_as); + add_shortcut_to_tooltip(ui->a_file_save); + add_shortcut_to_tooltip(ui->a_redo); + add_shortcut_to_tooltip(ui->a_undo); + add_shortcut_to_tooltip(ui->a_mesh_next); + add_shortcut_to_tooltip(ui->a_mesh_prev); + add_shortcut_to_tooltip(ui->tb_show_selected); + add_shortcut_to_tooltip(ui->tb_show_displayed); + add_shortcut_to_tooltip(ui->tb_show_everything); + add_shortcut_to_tooltip(ui->tb_curved_wires); + add_shortcut_to_tooltip(ui->tb_direct_wires); + add_shortcut_to_tooltip(ui->tb_show_all_mesh); + add_shortcut_to_tooltip(ui->tb_show_vertical_mesh); + add_shortcut_to_tooltip(ui->tb_show_horizontal_mesh); + add_shortcut_to_tooltip(ui->tb_show_no_mesh); + add_shortcut_to_tooltip(ui->tb_plane_zx, QKeySequence::listToString({ QKeySequence(Qt::Key_PageUp), QKeySequence(Qt::Key_PageDown) })); + add_shortcut_to_tooltip(ui->tb_plane_yz, QKeySequence::listToString({ QKeySequence(Qt::Key_PageUp), QKeySequence(Qt::Key_PageDown) })); + add_shortcut_to_tooltip(ui->tb_plane_xy, QKeySequence::listToString({ QKeySequence(Qt::Key_PageUp), QKeySequence(Qt::Key_PageDown) })); + Progress::singleton().register_impl_builder( [this](std::size_t max, std::string const& message) { return std::make_unique(ui->statusBar, max, @@ -91,6 +134,7 @@ bool MainWindow::parse_and_display() { if(auto res = oemsh.parse() ; !res.has_value()) { QGuiApplication::restoreOverrideCursor(); + update_board_dependant_buttons_visibility(false); log({ .level = Logger::Level::ERROR, .user_actions = { Logger::UserAction::OK }, @@ -102,6 +146,7 @@ bool MainWindow::parse_and_display() { return false; } + update_board_dependant_buttons_visibility(true); update_title(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); @@ -313,7 +358,7 @@ static QString const format_filter_csx("OpenEMS CSX file (*.csx *.xml)"); //****************************************************************************** void MainWindow::on_a_file_open_triggered() { - QFileDialog dialog(this, ui->a_file_open->toolTip()); + QFileDialog dialog(this, ui->a_file_open->text()); dialog.setAcceptMode(QFileDialog::AcceptOpen); dialog.setFileMode(QFileDialog::ExistingFile); dialog.setNameFilter(format_filter_csx); @@ -397,7 +442,7 @@ void MainWindow::on_a_file_save_triggered() { //****************************************************************************** void MainWindow::on_a_file_save_as_triggered() { - QFileDialog dialog(this, ui->a_file_save_as->toolTip()); + QFileDialog dialog(this, ui->a_file_save_as->text()); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setFileMode(QFileDialog::AnyFile); dialog.setNameFilter(format_filter_csx); @@ -414,6 +459,71 @@ void MainWindow::on_a_file_save_as_triggered() { } } +//****************************************************************************** +void MainWindow::on_a_appcsxcad_triggered() { + auto* file = new QTemporaryFile(this); + std::filesystem::path file_name(csx_file.toStdString()); +// file->setAutoRemove(true); + file->setFileTemplate(QString("%1/%2.oemsh.XXXXXX%3") + .arg(QDir::tempPath()) + .arg(QString::fromStdString(file_name.stem().generic_string())) + .arg(QString::fromStdString(file_name.extension().generic_string()))); + if(!file->open()) { + log({ + .level = Logger::Level::ERROR, + .user_actions = { Logger::UserAction::OK }, + .message = std::format( + "Failed to create temporary file {}", + file->fileName().toStdString()) + }); + return; + } + file->close(); + + auto output_backup = oemsh.get_params().output; + auto output_format_backup = oemsh.get_params().output_format; + oemsh.set_output(file->fileName().toStdString()); + oemsh.set_output_format(app::OpenEMSH::Params::OutputFormat::CSX); + auto res = oemsh.write(); + oemsh.set_output(output_backup); + oemsh.set_output_format(output_format_backup); + if(res.has_value()) { + log({ + .destination = Logger::id("Cli"), + .level = Logger::Level::INFO, + .message = std::format( + "Saved temporary file \"{}\"", + file->fileName().toStdString()) + }); + } else { + log({ + .level = Logger::Level::ERROR, + .user_actions = { Logger::UserAction::OK }, + .message = std::format( + "Failed to save temporary file \"{}\" : {}", + file->fileName().toStdString(), + res.error()) + }); + return; + } + + auto* p = new QProcess(this); + p->setProgram("AppCSXCAD"); + p->setArguments({ "--disableEdit", file->fileName() }); + connect(p, &QProcess::errorOccurred, [](QProcess::ProcessError error) { + if(error == QProcess::FailedToStart) + log({ + .level = Logger::Level::ERROR, + .user_actions = { Logger::UserAction::OK }, + .message = "Failed to run AppCSXCAD" + }); + }); + connect(this, &QObject::destroyed, [p](QObject* /*obj*/) { + disconnect(p, &QProcess::errorOccurred, nullptr, nullptr); + }); + p->start(); +} + //****************************************************************************** void MainWindow::on_a_edit_triggered() { auto* widget = static_cast(ui->toolBar->widgetForAction(ui->a_edit)); @@ -538,6 +648,37 @@ void MainWindow::handle_edition_from(app::Step from, std::function cons run(from); } +//****************************************************************************** +void MainWindow::update_board_dependant_buttons_visibility(bool are_enabled) { + ui->a_file_save->setEnabled(are_enabled); + ui->a_file_save_as->setEnabled(are_enabled); + ui->a_edit->setEnabled(are_enabled); + ui->a_mesh_prev->setEnabled(are_enabled); + ui->a_mesh_next->setEnabled(are_enabled); + ui->a_undo->setEnabled(are_enabled); + ui->a_redo->setEnabled(are_enabled); + ui->a_appcsxcad->setEnabled(are_enabled); + ui->tb_show_all_mesh->setEnabled(are_enabled); + ui->tb_show_horizontal_mesh->setEnabled(are_enabled); + ui->tb_show_vertical_mesh->setEnabled(are_enabled); + ui->tb_show_no_mesh->setEnabled(are_enabled); + ui->tb_show_selected->setEnabled(are_enabled); + ui->tb_show_displayed->setEnabled(are_enabled); + ui->tb_show_everything->setEnabled(are_enabled); + ui->tb_curved_wires->setEnabled(are_enabled); + ui->tb_direct_wires->setEnabled(are_enabled); + ui->tb_structure_rotate_cw->setEnabled(are_enabled); + ui->tb_structure_rotate_ccw->setEnabled(are_enabled); + ui->tb_structure_zoom_in->setEnabled(are_enabled); + ui->tb_structure_zoom_out->setEnabled(are_enabled); + ui->tb_processing_zoom_in->setEnabled(are_enabled); + ui->tb_processing_zoom_out->setEnabled(are_enabled); + ui->tb_plane_xy->setEnabled(are_enabled); + ui->tb_plane_yz->setEnabled(are_enabled); + ui->tb_plane_zx->setEnabled(are_enabled); + ui->tb_anchor->setEnabled(are_enabled); +} + //****************************************************************************** void MainWindow::update_navigation_buttons_visibility() { ui->a_undo->setEnabled(Caretaker::singleton().can_undo()); @@ -566,47 +707,7 @@ void MainWindow::update_show_buttons_pressing() { //****************************************************************************** void MainWindow::keyPressEvent(QKeyEvent* event) { - if(event->key() == Qt::Key_E || event->key() == Qt::Key_Space) { - on_a_edit_triggered(); - } else if(event->key() == Qt::Key_F) { - on_a_fit_triggered(); - } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_O) { - on_a_file_open_triggered(); - } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_S) { - if(event->modifiers() & Qt::ShiftModifier) { - on_a_file_save_as_triggered(); - } else { - on_a_file_save_triggered(); - } - } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Z) { - if(event->modifiers() & Qt::ShiftModifier) { - on_a_redo_triggered(); - } else { - on_a_undo_triggered(); - } - } else if(event->key() == Qt::Key_Greater) { - on_a_mesh_next_triggered(); - } else if(event->key() == Qt::Key_Less) { - on_a_mesh_prev_triggered(); - } else if(event->key() == Qt::Key_1) { - ui->tb_show_selected->click(); - } else if(event->key() == Qt::Key_2) { - ui->tb_show_displayed->click(); - } else if(event->key() == Qt::Key_3) { - ui->tb_show_everything->click(); - } else if(event->key() == Qt::Key_C) { - ui->tb_curved_wires->click(); - } else if(event->key() == Qt::Key_D) { - ui->tb_direct_wires->click(); - } else if(event->key() == Qt::Key_X) { - ui->tb_show_all_mesh->click(); - } else if(event->key() == Qt::Key_V) { - ui->tb_show_vertical_mesh->click(); - } else if(event->key() == Qt::Key_H) { - ui->tb_show_horizontal_mesh->click(); - } else if(event->key() == Qt::Key_Period) { - ui->tb_show_no_mesh->click(); - } else if(event->key() == Qt::Key_PageUp) { + if(event->key() == Qt::Key_PageUp) { if(auto const* b = ui->bg_plane->checkedButton() ; b == ui->tb_plane_xy) { ui->tb_plane_zx->click(); diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index 26ae48a2..29c43fe1 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -34,6 +34,7 @@ class MainWindow : public QMainWindow { void set_style(Style const& style); void update_title(); + void update_board_dependant_buttons_visibility(bool are_enabled); void update_navigation_buttons_visibility(); void update_show_buttons_pressing(); void update_cell_number(bool reset = false); @@ -79,6 +80,7 @@ private slots: void on_a_mesh_next_triggered(); void on_a_undo_triggered(); void on_a_redo_triggered(); + void on_a_appcsxcad_triggered(); void on_a_does_use_csx_properties_color_triggered(); void edit_global_params(); diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index 66ccab2b..b12a3974 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -100,6 +100,9 @@ Show selected chain only + + 1 + true @@ -128,6 +131,9 @@ Show axes/plane currently displayed only + + 2 + true @@ -153,6 +159,9 @@ Show everything + + 3 + true @@ -188,6 +197,9 @@ Curved wires + + C + true @@ -216,6 +228,9 @@ Direct wires + + D + true @@ -396,6 +411,9 @@ Show mesh + + X + true @@ -424,6 +442,9 @@ Show vertical meshlines only + + V + true @@ -449,6 +470,9 @@ Show horizontal meshlines only + + H + true @@ -474,6 +498,9 @@ Don't show mesh + + . + true @@ -657,6 +684,7 @@ + @@ -700,16 +728,22 @@ Fit view + + F + - Open + Open CSX file... - Open CSX file + Open CSX file... + + + QKeySequence::StandardKey::Open @@ -722,6 +756,9 @@ Save mesh overwriting input CSX file + + QKeySequence::StandardKey::Save + @@ -733,6 +770,9 @@ Save mesh as... + + QKeySequence::StandardKey::SaveAs + @@ -744,6 +784,9 @@ Edit parameters + + E + @@ -755,6 +798,9 @@ Run the next meshing step + + Qt::Key::Key_Greater + @@ -766,6 +812,9 @@ Go back to previous meshing step + + Qt::Key::Key_Less + @@ -777,6 +826,9 @@ Undo + + QKeySequence::StandardKey::Undo + @@ -788,10 +840,27 @@ Redo + + QKeySequence::StandardKey::Redo + + + + AppCSXCAD + + + Preview mesh in AppCSXCAD + + + A + + + :/qcsxcad.png + + Use CSX Properties colors diff --git a/src/ui/qt/resources.qrc b/src/ui/qt/resources.qrc index f0f479ab..36f96097 100644 --- a/src/ui/qt/resources.qrc +++ b/src/ui/qt/resources.qrc @@ -2,5 +2,6 @@ ../../../icon/openemsh.128.png ../../../icon/openemsh.ico + ../../../icon/qcsxcad.png diff --git a/src/ui/qt/style.cpp b/src/ui/qt/style.cpp index 5b3f1b74..72d798ce 100644 --- a/src/ui/qt/style.cpp +++ b/src/ui/qt/style.cpp @@ -125,7 +125,8 @@ std::vector