From a4610442ae189f9460b80d6557573702d0b13f5a Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sat, 17 Jan 2026 01:56:05 +0100 Subject: [PATCH 1/9] add AppCSXCAD preview --- icon/qcsxcad.png | Bin 0 -> 14030 bytes src/ui/qt/main_window.cpp | 69 ++++++++++++++++++++++++++++++++++++++ src/ui/qt/main_window.hpp | 1 + src/ui/qt/main_window.ui | 12 +++++++ src/ui/qt/resources.qrc | 1 + 5 files changed, 83 insertions(+) create mode 100644 icon/qcsxcad.png diff --git a/icon/qcsxcad.png b/icon/qcsxcad.png new file mode 100644 index 0000000000000000000000000000000000000000..a1530be57c2db836bda804636888eac4b58b6753 GIT binary patch literal 14030 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 AX8-^I literal 0 HcmV?d00001 diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 7fe2ff0e..92f30c81 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 @@ -414,6 +416,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, [this](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)); @@ -570,6 +637,8 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { on_a_edit_triggered(); } else if(event->key() == Qt::Key_F) { on_a_fit_triggered(); + } else if(event->key() == Qt::Key_A) { + on_a_appcsxcad_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) { diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index 26ae48a2..639a43e4 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -79,6 +79,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..657da2af 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -657,6 +657,7 @@ + @@ -792,6 +793,17 @@ + + + AppCSXCAD + + + Preview mesh in AppCSXCAD + + + :/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 From e4370812075438e13efcff079e68451b4a71bca3 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sat, 17 Jan 2026 02:54:43 +0100 Subject: [PATCH 2/9] nix : set QCSXCAD icon to AppCSXCAD --- flake.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/flake.nix b/flake.nix index 35ab78da..0b617d5d 100644 --- a/flake.nix +++ b/flake.nix @@ -247,6 +247,12 @@ nativeBuildInputs = old.nativeBuildInputs ++ [ prev.libsForQt5.wrapQtAppsHook ]; + prePatch = '' + # Set icon + # TODO add Windows RC file + sed -i AppCSXCAD.cpp \ + -e '/^ QString title = tr("AppCSXCAD");/a\ setWindowIcon(QPixmap(":/images/QCSXCAD_Icon.png"));' + ''; })).override { mkDerivation = prev.fastStdenv.mkDerivation; inherit (final) csxcad qcsxcad; From 25f8fe560ac6e22fc2f112c8c0127d5e39981fbd Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sat, 17 Jan 2026 03:18:45 +0100 Subject: [PATCH 3/9] nix : add OpenEMSH button to AppCSXCAD --- flake.nix | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 0b617d5d..2fa1d610 100644 --- a/flake.nix +++ b/flake.nix @@ -238,6 +238,25 @@ nativeBuildInputs = old.nativeBuildInputs ++ [ prev.libsForQt5.wrapQtAppsHook ]; + prePatch = '' + # Allow to open .csx files + substituteInPlace QCSXCAD.cpp --replace-fail 'XML-File (*.xml)' 'XML-File (*.xml *.csx)' + + # Open OEMSH + cp ${self}/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,11 +266,48 @@ nativeBuildInputs = old.nativeBuildInputs ++ [ prev.libsForQt5.wrapQtAppsHook ]; - prePatch = '' + 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; From d45010b47c93348bf44b0754d968c0471c83b223 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sat, 17 Jan 2026 03:31:44 +0100 Subject: [PATCH 4/9] support both /openEMS/ContinuousStructure & /ContinuousStructure paths in CSX file --- src/infra/parsers/parser_from_csx.cpp | 22 ++++++++++++++++----- src/infra/serializers/serializer_to_csx.cpp | 9 +++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/infra/parsers/parser_from_csx.cpp b/src/infra/parsers/parser_from_csx.cpp index 48ddb95a..b3a33de6 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) -> string { + 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(); From 5b442ddbfd60a6c7d999ff52c2d5f327b0bae056 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sat, 17 Jan 2026 03:37:31 +0100 Subject: [PATCH 5/9] GUI : disable most of buttons when board is not ready --- src/ui/qt/main_window.cpp | 55 ++++++++++++++++++++++++++++++++------- src/ui/qt/main_window.hpp | 1 + 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 92f30c81..ca5be78c 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -52,6 +52,8 @@ 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); Progress::singleton().register_impl_builder( @@ -93,6 +95,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 }, @@ -104,6 +107,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()); @@ -605,6 +609,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()); @@ -634,29 +669,29 @@ 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(); + ui->a_edit->trigger(); } else if(event->key() == Qt::Key_F) { - on_a_fit_triggered(); + ui->a_fit->trigger(); } else if(event->key() == Qt::Key_A) { - on_a_appcsxcad_triggered(); + ui->a_appcsxcad->trigger(); } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_O) { - on_a_file_open_triggered(); + ui->a_file_open->trigger(); } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_S) { if(event->modifiers() & Qt::ShiftModifier) { - on_a_file_save_as_triggered(); + ui->a_file_save_as->trigger(); } else { - on_a_file_save_triggered(); + ui->a_file_save->trigger(); } } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Z) { if(event->modifiers() & Qt::ShiftModifier) { - on_a_redo_triggered(); + ui->a_redo->trigger(); } else { - on_a_undo_triggered(); + ui->a_undo->trigger(); } } else if(event->key() == Qt::Key_Greater) { - on_a_mesh_next_triggered(); + ui->a_mesh_next->trigger(); } else if(event->key() == Qt::Key_Less) { - on_a_mesh_prev_triggered(); + ui->a_mesh_prev->trigger(); } else if(event->key() == Qt::Key_1) { ui->tb_show_selected->click(); } else if(event->key() == Qt::Key_2) { diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index 639a43e4..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); From 7bc864d19838bd829ea56681b2ef31293960eeb4 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sun, 18 Jan 2026 16:31:52 +0100 Subject: [PATCH 6/9] GUI : set shortcut keys from .ui file & show in tooltips --- src/ui/qt/main_window.cpp | 87 +++++++++++++++++++-------------------- src/ui/qt/main_window.ui | 61 ++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 47 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index ca5be78c..0aee33b4 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -37,6 +37,21 @@ namespace ui::qt { using DisplayMode = ProcessingScene::DisplayMode; using MeshVisibility = StructureScene::MeshVisibility; +//****************************************************************************** +static QString get_shortcuts(QAction* p) { return QKeySequence::listToString(p->shortcuts()); } +static QString get_shortcuts(QToolButton* 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) @@ -56,6 +71,30 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) 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, @@ -319,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); @@ -403,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); @@ -668,49 +707,7 @@ void MainWindow::update_show_buttons_pressing() { //****************************************************************************** void MainWindow::keyPressEvent(QKeyEvent* event) { - if(event->key() == Qt::Key_E || event->key() == Qt::Key_Space) { - ui->a_edit->trigger(); - } else if(event->key() == Qt::Key_F) { - ui->a_fit->trigger(); - } else if(event->key() == Qt::Key_A) { - ui->a_appcsxcad->trigger(); - } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_O) { - ui->a_file_open->trigger(); - } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_S) { - if(event->modifiers() & Qt::ShiftModifier) { - ui->a_file_save_as->trigger(); - } else { - ui->a_file_save->trigger(); - } - } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Z) { - if(event->modifiers() & Qt::ShiftModifier) { - ui->a_redo->trigger(); - } else { - ui->a_undo->trigger(); - } - } else if(event->key() == Qt::Key_Greater) { - ui->a_mesh_next->trigger(); - } else if(event->key() == Qt::Key_Less) { - ui->a_mesh_prev->trigger(); - } 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.ui b/src/ui/qt/main_window.ui index 657da2af..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 @@ -701,16 +728,22 @@ Fit view + + F + - Open + Open CSX file... - Open CSX file + Open CSX file... + + + QKeySequence::StandardKey::Open @@ -723,6 +756,9 @@ Save mesh overwriting input CSX file + + QKeySequence::StandardKey::Save + @@ -734,6 +770,9 @@ Save mesh as... + + QKeySequence::StandardKey::SaveAs + @@ -745,6 +784,9 @@ Edit parameters + + E + @@ -756,6 +798,9 @@ Run the next meshing step + + Qt::Key::Key_Greater + @@ -767,6 +812,9 @@ Go back to previous meshing step + + Qt::Key::Key_Less + @@ -778,6 +826,9 @@ Undo + + QKeySequence::StandardKey::Undo + @@ -789,6 +840,9 @@ Redo + + QKeySequence::StandardKey::Redo + @@ -800,6 +854,9 @@ Preview mesh in AppCSXCAD + + A + :/qcsxcad.png From 38ea365eba7c1a049bc4eb54fbac4906fd632e2f Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sun, 18 Jan 2026 19:47:58 +0100 Subject: [PATCH 7/9] GUI : use openEMS blue for shape color --- src/ui/qt/style.cpp | 3 ++- src/ui/qt/style.hpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) 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