From 6af8e0b380df507149624b557f6aecbf4e57c617 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Sun, 5 Oct 2025 13:44:48 +0200 Subject: [PATCH 1/2] Gui: Add a possibility to redirect macro commands --- src/Gui/Macro.cpp | 12 ++++++++++- src/Gui/Macro.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/Gui/Macro.cpp b/src/Gui/Macro.cpp index 49713f5ff9..f68c4e7b37 100644 --- a/src/Gui/Macro.cpp +++ b/src/Gui/Macro.cpp @@ -218,6 +218,8 @@ MacroManager::~MacroManager() this->params->Detach(this); } +std::stack> MacroManager::redirectFuncs; + void MacroManager::OnChange(Base::Subject &rCaller, const char * sReason) { (void)rCaller; @@ -268,8 +270,16 @@ void MacroManager::addPendingLine(LineType type, const char* line) void MacroManager::addLine(LineType Type, const char* sLine) { - if (!sLine) + if (!sLine) { return; + } + + std::function redirectFunc = + redirectFuncs.empty() ? nullptr : redirectFuncs.top(); + if (redirectFunc) { + redirectFunc(Type, sLine); + return; + } if (buffer.hasPendingLines()) { if (buffer.addPendingLineIfComment(Type, sLine)) { diff --git a/src/Gui/Macro.h b/src/Gui/Macro.h index eeff49f568..47e5c59829 100644 --- a/src/Gui/Macro.h +++ b/src/Gui/Macro.h @@ -24,6 +24,7 @@ #ifndef GUI_MACRO_H #define GUI_MACRO_H +#include #include #include #include @@ -121,6 +122,46 @@ public: Cmt, /**< The line is handled as a comment */ }; + /** Redirect the macro output temporarily. + * + * This is a RAII class to redirect the macro output to a function. + * Initializing an instance with a redirect function, ensures that + * MacroManager::addLine() calls are redirected by means of the redirect + * function. More than one instances can be used, overriding earlier + * instances and their redirect functions. + * + * @code + * void myRedirectFunc1(MacroManager::LineType type, const char* line) + * { // do something with the line } + * + * void myRedirectFunc2(MacroManager::LineType type, const char* line) + * { // do something else with the line } + * + * { + * MacroRedirector redirect(myRedirectFunc1); + * // all macro output will go to myRedirectFunc1 + * { + * MacroRedirector redirect2(myRedirectFunc2); + * // all macro output will go to myRedirectFunc2 + * } + * // all macro output will go to myRedirectFunc1 + * } + * // normal macro output is restored + * @endcode + */ + class MacroRedirector + { + public: + explicit MacroRedirector(const std::function& func) + { + MacroManager::redirectFuncs.push(func); + } + + ~MacroRedirector() { + MacroManager::redirectFuncs.pop(); + } + }; + /** Opens a new Macro recording session * Starts a session with the type and the name of the macro. * All user interactions will be recorded as long as the commit() or cancel() isn't called. @@ -139,7 +180,15 @@ public: bool isOpen() const { return macroFile.isOpen(); } - /// insert a new line in the macro + + /** Insert a new line in the macro. + * + * The line is added to the macro unless the output is redirected. + * @see MacroRedirector. + * + * @param Type The type of the line + * @param sLine The line to add + */ void addLine(LineType Type, const char* sLine); /// insert a new pending line in the macro void addPendingLine(LineType type, const char* line); @@ -174,6 +223,7 @@ private: mutable PythonConsole* pyConsole{nullptr}; // link to the python console PythonDebugger* pyDebugger; Base::Reference params; // link to the Macro parameter group + static std::stack> redirectFuncs; friend struct ApplicationP; }; From 9a09e8e321cbe25b2967e935ba4cc881b0ba43f2 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Sun, 5 Oct 2025 13:46:12 +0200 Subject: [PATCH 2/2] Gui: Fix console messages Add Property dialog --- src/Gui/Dialogs/DlgAddProperty.cpp | 91 ++++++++++++++++++++++-------- src/Gui/Dialogs/DlgAddProperty.h | 16 +++++- 2 files changed, 79 insertions(+), 28 deletions(-) diff --git a/src/Gui/Dialogs/DlgAddProperty.cpp b/src/Gui/Dialogs/DlgAddProperty.cpp index 34dd2f4e34..be048a8503 100644 --- a/src/Gui/Dialogs/DlgAddProperty.cpp +++ b/src/Gui/Dialogs/DlgAddProperty.cpp @@ -26,6 +26,7 @@ # include # include # include +#include #include #include @@ -37,6 +38,8 @@ #include #include "Dialogs/DlgAddProperty.h" +#include "Application.h" +#include "Macro.h" #include "ui_DlgAddProperty.h" #include "ViewProviderVarSet.h" #include "propertyeditor/PropertyItem.h" @@ -58,7 +61,8 @@ const std::string DlgAddProperty::GroupBase = "Base"; * - keep the value if the name of the property is changed, * - support units (see #15557), * - support enumerations (see #15553), - * - make OK available as soon as there is a valid property (see #17474), and + * - make OK available as soon as there is a valid property (see #17474), + * - useful Python console commands (see #23760), * - support expressions (see #19716). * * Especially supporting expressions in the value field makes the logic @@ -145,21 +149,29 @@ DlgAddProperty::DlgAddProperty(QWidget* parent, DlgAddProperty::DlgAddProperty(QWidget* parent, App::PropertyContainer* container, ViewProviderVarSet* viewProvider) - : QDialog(parent), - container(container), - ui(new Ui_DlgAddProperty), - comboBoxGroup(this), - completerType(this), - editor(nullptr), - transactionID(0) + : QDialog(parent) + , container(container) + , ui(new Ui_DlgAddProperty) + , comboBoxGroup(this) + , completerType(this) + , editor(nullptr) + , transactionID(0) { ui->setupUi(this); - + setupMacroRedirector(); initializeWidgets(viewProvider); } DlgAddProperty::~DlgAddProperty() = default; +void DlgAddProperty::setupMacroRedirector() +{ + setValueRedirector = std::make_unique([this](MacroManager::LineType /*type*/, + const char* line) { + this->setValueCommand = line; + }); +} + int DlgAddProperty::findLabelRow(const char* labelName, QFormLayout* layout) { for (int row = 0; row < layout->rowCount(); ++row) { @@ -337,9 +349,8 @@ void DlgAddProperty::addEnumEditor(PropertyItem* propertyItem) void DlgAddProperty::addNormalEditor(PropertyItem* propertyItem) { - editor.reset(propertyItem->createEditor(this, [this]() { - this->valueChanged(); - }, FrameOption::WithFrame)); + editor.reset(propertyItem->createEditor(this, []() {}, + FrameOption::WithFrame)); } void DlgAddProperty::addEditor(PropertyItem* propertyItem) @@ -651,11 +662,6 @@ void DlgAddProperty::setEditor(bool valueNeedsReset) else { initializeValue(); } - - if (editor) { - QVariant data = propertyItem->editorData(editor.get()); - propertyItem->setData(data); - } } void DlgAddProperty::setPropertyItem(App::Property* prop, bool supportsExpressions) @@ -800,12 +806,6 @@ void DlgAddProperty::valueChangedEnum() propEnum->setEnums(enumValuesVec); } -void DlgAddProperty::valueChanged() -{ - QVariant data = propertyItem->editorData(editor.get()); - propertyItem->setData(data); -} - /* We use these functions rather than the functions provided by App::Document * because this dialog may be opened when another transaction is in progress. * An example is opening a sketch. If this dialog uses the functions provided @@ -826,6 +826,29 @@ void DlgAddProperty::critical(const QString& title, const QString& text) { } } +void DlgAddProperty::recordMacroAdd(const App::PropertyContainer* container, + const std::string& type, const std::string& name, + const std::string& group, const std::string& doc) const +{ + std::ostringstream command; + command << "App.getDocument('"; + const App::Document* document = freecad_cast(container); + const App::DocumentObject* object = freecad_cast(container); + if (document) { + command << document->getName() << "')"; + } + else if (object) { + command << object->getDocument()->getName() << "')." << object->getNameInDocument(); + } + else { + FC_ERR("Cannot record macro for container of type " << container->getTypeId().getName()); + return; + } + command << ".addProperty('" << type << "', '" << name << "', '" << + group << "', '" << doc + "')"; + Application::Instance->macroManager()->addLine(Gui::MacroManager::App, command.str().c_str()); +} + App::Property* DlgAddProperty::createProperty() { std::string name = ui->lineEditName->text().toStdString(); @@ -833,9 +856,16 @@ App::Property* DlgAddProperty::createProperty() std::string type = ui->comboBoxType->currentText().toStdString(); std::string doc = ui->lineEditToolTip->text().toStdString(); + auto recordAddCommand = [this](MacroManager::LineType, const char* line) { + this->addCommand = line; + }; + try { - return container->addDynamicProperty(type.c_str(), name.c_str(), - group.c_str(), doc.c_str()); + App::Property* prop = container->addDynamicProperty(type.c_str(), name.c_str(), + group.c_str(), doc.c_str()); + MacroManager::MacroRedirector redirector(recordAddCommand); + recordMacroAdd(container, type, name, group, doc); + return prop; } catch (Base::Exception& e) { e.reportException(); @@ -893,12 +923,23 @@ void DlgAddProperty::addDocumentation() { void DlgAddProperty::accept() { + if (editor) { + QVariant data = propertyItem->editorData(editor.get()); + propertyItem->setData(data); + } addDocumentation(); auto* object = freecad_cast(container); if (object) { object->ExpressionEngine.execute(); } closeTransaction(TransactionOption::Commit); + + setValueRedirector = nullptr; + Application::Instance->macroManager()->addLine(MacroManager::LineType::App, addCommand.c_str()); + Application::Instance->macroManager()->addLine(MacroManager::LineType::App, + setValueCommand.c_str()); + setupMacroRedirector(); + std::string group = comboBoxGroup.currentText().toStdString(); std::string type = ui->comboBoxType->currentText().toStdString(); auto paramGroup = App::GetApplication().GetParameterGroupByPath( diff --git a/src/Gui/Dialogs/DlgAddProperty.h b/src/Gui/Dialogs/DlgAddProperty.h index c2e0de1b69..325110f964 100644 --- a/src/Gui/Dialogs/DlgAddProperty.h +++ b/src/Gui/Dialogs/DlgAddProperty.h @@ -36,6 +36,7 @@ #include #include "propertyeditor/PropertyItem.h" +#include "Macro.h" namespace Gui { @@ -94,7 +95,6 @@ public: QLayout* layout); public Q_SLOTS: - void valueChanged(); void valueChangedEnum(); private: @@ -108,8 +108,11 @@ private: Type }; - DlgAddProperty(QWidget* parent, App::PropertyContainer* container, - ViewProviderVarSet* viewProvider); + DlgAddProperty(QWidget* parent, + App::PropertyContainer* container, + ViewProviderVarSet* viewProvider); + + void setupMacroRedirector(); void initializeGroup(); @@ -156,6 +159,9 @@ private: void openTransaction(); void critical(const QString& title, const QString& text); + void recordMacroAdd(const App::PropertyContainer* container, + const std::string& type, const std::string& name, + const std::string& group, const std::string& doc) const; App::Property* createProperty(); void closeTransaction(TransactionOption option); void clearFields(); @@ -181,6 +187,10 @@ private: QMetaObject::Connection connComboBoxGroup; QMetaObject::Connection connComboBoxType; QMetaObject::Connection connLineEditNameTextChanged; + + std::unique_ptr setValueRedirector; + std::string addCommand; + std::string setValueCommand; }; } // namespace Dialog