Merge pull request #24446 from pieterhijma/fix-add-prop-dialog-py-console

Gui: Fix Python Console messages Add Property dialog
This commit is contained in:
Chris Hennes
2025-10-09 10:29:37 -05:00
committed by GitHub
4 changed files with 141 additions and 30 deletions

View File

@@ -26,6 +26,7 @@
# include <QString>
# include <QCompleter>
# include <algorithm>
#include <memory>
#include <App/Application.h>
#include <App/Document.h>
@@ -37,6 +38,8 @@
#include <Base/Tools.h>
#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<MacroManager::MacroRedirector>([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)
@@ -652,11 +663,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)
@@ -801,12 +807,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
@@ -827,6 +827,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<App::Document*>(container);
const App::DocumentObject* object = freecad_cast<App::DocumentObject*>(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();
@@ -834,9 +857,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();
@@ -894,12 +924,23 @@ void DlgAddProperty::addDocumentation() {
void DlgAddProperty::accept()
{
if (editor) {
QVariant data = propertyItem->editorData(editor.get());
propertyItem->setData(data);
}
addDocumentation();
auto* object = freecad_cast<App::DocumentObject*>(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(

View File

@@ -36,6 +36,7 @@
#include <App/PropertyContainer.h>
#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<MacroManager::MacroRedirector> setValueRedirector;
std::string addCommand;
std::string setValueCommand;
};
} // namespace Dialog

View File

@@ -218,6 +218,8 @@ MacroManager::~MacroManager()
this->params->Detach(this);
}
std::stack<std::function<void(MacroManager::LineType, const char*)>> MacroManager::redirectFuncs;
void MacroManager::OnChange(Base::Subject<const char*> &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<void(LineType, const char*)> redirectFunc =
redirectFuncs.empty() ? nullptr : redirectFuncs.top();
if (redirectFunc) {
redirectFunc(Type, sLine);
return;
}
if (buffer.hasPendingLines()) {
if (buffer.addPendingLineIfComment(Type, sLine)) {

View File

@@ -24,6 +24,7 @@
#ifndef GUI_MACRO_H
#define GUI_MACRO_H
#include <stack>
#include <tuple>
#include <QString>
#include <QStringList>
@@ -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<void(LineType, const char*)>& 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<ParameterGrp> params; // link to the Macro parameter group
static std::stack<std::function<void(LineType, const char*)>> redirectFuncs;
friend struct ApplicationP;
};