FreeCAD / src /Tools /params_utils.py
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * Copyright (c) 2022 Zheng Lei (realthunder) <realthunder.dev@gmail.com>*
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Utilities for generating C++ code for parameter management using Python Cog"""
import cog
import inspect
import re
from os import path
def quote(txt, indent=0):
lines = [
" " * indent + '"' + l.replace("\\", "\\\\").replace('"', '\\"') for l in txt.split("\n")
]
return '\\n"\n'.join(lines) + '"'
def init_params(params, namespace, class_name, param_path, header_file=None):
for param in params:
param.path = param_path
if not header_file:
header_file = [f"{namespace}/{class_name}.h"]
param.header_file = header_file + getattr(param.proxy, "header_file", [])
param.namespace = namespace
param.class_name = class_name
return params
def auto_comment(frame=1, msg=None, count=1):
trace = []
for stack in inspect.stack()[frame : frame + count]:
filename = path.normpath(stack[1]).split("/src/")[-1]
if filename.find("<") >= 0:
break
lineno = stack[2]
trace.insert(0, f"{filename}:{lineno}")
return f'{"// Auto generated code" if msg is None else msg} ({" <- ".join(trace)})'
def trace_comment():
return auto_comment(2)
def get_module_path(module):
return path.dirname(module.__file__.split(f"src{path.sep}")[-1])
def declare_begin(module, header=True):
class_name = module.ClassName
namespace = module.NameSpace
params = module.Params
param_path = module.ParamPath
file_path = getattr(module, "FilePath", get_module_path(module))
param_file = getattr(module, "ParamSource", f"{file_path}/{class_name}.py")
header_file = getattr(module, "HeaderFile", f"{file_path}/{class_name}.h")
source_file = getattr(module, "SourceFile", f"{file_path}/{class_name}.cpp")
class_doc = module.ClassDoc
signal = getattr(module, "Signal", False)
if header:
cog.out(
f"""
{trace_comment()}
#include <Base/Parameter.h>
{"#include <fastsignals/signal.h>" if signal else ""}
"""
)
cog.out(
f"""
{trace_comment()}
namespace {namespace} {{
/** {class_doc}
* The parameters are under group "{param_path}"
*
* This class is auto generated by {param_file}. Modify that file
* instead of this one, if you want to add any parameter. You need
* to install Cog Python package for code generation:
* @code
* pip install cogapp
* @endcode
*
* Once modified, you can regenerate the header and the source file,
* @code
* python3 -m cogapp -r {header_file} {source_file}
* @endcode
*
* You can add a new parameter by adding lines in {param_file}. Available
* parameter types are 'Int, UInt, String, Bool, Float'. For example, to add
* a new Int type parameter,
* @code
* ParamInt(parameter_name, default_value, documentation, on_change=False)
* @endcode
*
* If there is special handling on parameter change, pass in on_change=True.
* And you need to provide a function implementation in {source_file} with
* the following signature.
* @code
* void {class_name}:on<parameter_name>Changed()
* @endcode
*/
class {namespace}Export {class_name} {{
public:
static ParameterGrp::handle getHandle();
"""
)
if signal:
cog.out(
f"""
static fastsignals::signal<void (const char*)> &signalParamChanged();
static void signalAll();
"""
)
for param in params:
cog.out(
f"""
{trace_comment()}
//@{{
/// Accessor for parameter {param.name}"""
)
if param._doc:
cog.out(
f"""
///"""
)
for line in param._doc.split("\n"):
cog.out(
f"""
/// {line}"""
)
cog.out(
f"""
static const {param.C_Type} & get{param.name}();
static const {param.C_Type} & default{param.name}();
static void remove{param.name}();
static void set{param.name}(const {param.C_Type} &v);
static const char *doc{param.name}();"""
)
if param.on_change:
cog.out(
f"""
static void on{param.name}Changed();"""
)
cog.out(
f"""
//@}}
"""
)
def declare_end(module):
class_name = module.ClassName
namespace = module.NameSpace
cog.out(
f"""
{trace_comment()}
}}; // class {class_name}
}} // namespace {namespace}
"""
)
def define(module, header=True):
class_name = module.ClassName
namespace = module.NameSpace
params = module.Params
param_path = module.ParamPath
class_doc = module.ClassDoc
signal = getattr(module, "Signal", False)
if header:
cog.out(
f"""
{trace_comment()}
#include <unordered_map>
#include <App/Application.h>
#include <App/DynamicProperty.h>
#include "{class_name}.h"
using namespace {namespace};
"""
)
cog.out(
f"""
{trace_comment()}
namespace {{
class {class_name}P: public ParameterGrp::ObserverType {{
public:
ParameterGrp::handle handle;
std::unordered_map<const char *,void(*)({class_name}P*),App::CStringHasher,App::CStringHasher> funcs;
"""
)
if signal:
cog.out(
f"""
{trace_comment()}
fastsignals::signal<void (const char*)> signalParamChanged;
void signalAll()
{{"""
)
for param in params:
cog.out(
f"""
signalParamChanged("{param.name}");"""
)
cog.out(
f"""
{trace_comment()}
}}"""
)
for param in params:
cog.out(
f"""
{param.C_Type} {param.name};"""
)
cog.out(
f"""
{trace_comment()}
{class_name}P() {{
handle = App::GetApplication().GetParameterGroupByPath("{param_path}");
handle->Attach(this);
"""
)
for param in params:
cog.out(
f"""
{param.name} = {param.getter('handle')};
funcs["{param.name}"] = &{class_name}P::update{param.name};"""
)
cog.out(
f"""
}}
{trace_comment()}
~{class_name}P() {{
}}
"""
)
cog.out(
f"""
{trace_comment()}
void OnChange(Base::Subject<const char*> &, const char* sReason) {{
if(!sReason)
return;
auto it = funcs.find(sReason);
if(it == funcs.end())
return;
it->second(this);
{"signalParamChanged(sReason);" if signal else ""}
}}
"""
)
for param in params:
if not param.on_change:
cog.out(
f"""
{trace_comment()}
static void update{param.name}({class_name}P *self) {{
self->{param.name} = {param.getter('self->handle')};
}}"""
)
else:
cog.out(
f"""
{trace_comment()}
static void update{param.name}({class_name}P *self) {{
auto v = {param.getter('self->handle')};
if (self->{param.name} != v) {{
self->{param.name} = v;
{class_name}::on{param.name}Changed();
}}
}}"""
)
cog.out(
f"""
}};
{trace_comment()}
{class_name}P *instance() {{
static {class_name}P *inst = new {class_name}P;
return inst;
}}
}} // Anonymous namespace
"""
)
cog.out(
f"""
{trace_comment()}
ParameterGrp::handle {class_name}::getHandle() {{
return instance()->handle;
}}
"""
)
if signal:
cog.out(
f"""
{trace_comment()}
fastsignals::signal<void (const char*)> &
{class_name}::signalParamChanged() {{
return instance()->signalParamChanged;
}}
"""
)
cog.out(
f"""
{trace_comment()}
void signalAll() {{
instance()->signalAll();
}}
"""
)
for param in params:
cog.out(
f"""
{trace_comment()}
const char *{class_name}::doc{param.name}() {{
return {param.doc(class_name)};
}}
"""
)
cog.out(
f"""
{trace_comment()}
const {param.C_Type} & {class_name}::get{param.name}() {{
return instance()->{param.name};
}}
"""
)
cog.out(
f"""
{trace_comment()}
const {param.C_Type} & {class_name}::default{param.name}() {{
const static {param.C_Type} def = {param.default};
return def;
}}
"""
)
cog.out(
f"""
{trace_comment()}
void {class_name}::set{param.name}(const {param.C_Type} &v) {{
{param.setter()};
instance()->{param.name} = v;
}}
"""
)
cog.out(
f"""
{trace_comment()}
void {class_name}::remove{param.name}() {{
instance()->handle->Remove{param.Type}("{param.name}");
}}
"""
)
def widgets_declare(param_set):
param_group = param_set.ParamGroup
for title, params in param_group:
name = _regex.sub("", title)
cog.out(
f"""
{trace_comment()}
QGroupBox * group{name} = nullptr;"""
)
for param in params:
param.declare_widget()
def widgets_init(param_set):
param_group = param_set.ParamGroup
cog.out(
f"""
auto layout = new QVBoxLayout(this);"""
)
for title, params in param_group:
name = _regex.sub("", title)
cog.out(
f"""
{trace_comment()}
group{name} = new QGroupBox(this);
layout->addWidget(group{name});
auto layoutHoriz{name} = new QHBoxLayout(group{name});
auto layout{name} = new QGridLayout();
layoutHoriz{name}->addLayout(layout{name});
layoutHoriz{name}->addStretch();"""
)
for row, param in enumerate(params):
cog.out(
f"""
{trace_comment()}"""
)
param.init_widget(row, name)
cog.out(
"""
layout->addItem(new QSpacerItem(40, 20, QSizePolicy::Fixed, QSizePolicy::Expanding));
retranslateUi();"""
)
def widgets_restore(param_set):
param_group = param_set.ParamGroup
cog.out(
f"""
{trace_comment()}"""
)
for _, params in param_group:
for param in params:
param.widget_restore()
def widgets_save(param_set):
param_group = param_set.ParamGroup
cog.out(
f"""
{trace_comment()}"""
)
for _, params in param_group:
for param in params:
param.widget_save()
def preference_dialog_declare_begin(param_set, header=True):
namespace = param_set.NameSpace
class_name = param_set.ClassName
dialog_namespace = getattr(param_set, "DialogNameSpace", "Dialog")
param_group = param_set.ParamGroup
file_path = getattr(param_set, "FilePath", get_module_path(param_set))
param_file = getattr(param_set, "ParamSource", f"{file_path}/{class_name}.py")
header_file = getattr(param_set, "HeaderFile", f"{file_path}/{class_name}.h")
source_file = getattr(param_set, "SourceFile", f"{file_path}/{class_name}.cpp")
class_doc = param_set.ClassDoc
if header:
cog.out(
f"""
{trace_comment()}
#include <Gui/PropertyPage.h>
#include <Gui/PrefWidgets.h>"""
)
cog.out(
f"""
{trace_comment()}
class QLabel;
class QGroupBox;
namespace {namespace} {{
namespace {dialog_namespace} {{
/** {class_doc}
* This class is auto generated by {param_file}. Modify that file
* instead of this one, if you want to make any change. You need
* to install Cog Python package for code generation:
* @code
* pip install cogapp
* @endcode
*
* Once modified, you can regenerate the header and the source file,
* @code
* python3 -m cogapp -r {header_file} {source_file}
* @endcode
*/
class {class_name} : public Gui::Dialog::PreferencePage
{{
Q_OBJECT
public:
{class_name}( QWidget* parent = 0 );
~{class_name}();
void saveSettings();
void loadSettings();
void retranslateUi();
protected:
void changeEvent(QEvent *e);
private:"""
)
widgets_declare(param_set)
def preference_dialog_declare_end(param_set):
class_name = param_set.ClassName
namespace = param_set.NameSpace
dialog_namespace = getattr(param_set, "DialogNameSpace", "Dialog")
cog.out(
f"""
{trace_comment()}
}};
}} // namespace {dialog_namespace}
}} // namespace {namespace}
"""
)
def preference_dialog_declare(param_set, header=True):
preference_dialog_declare_begin(param_set, header)
preference_dialog_declare_end(param_set)
_regex = re.compile(r"[^a-zA-Z_]")
def preference_dialog_define(param_set, header=True):
param_group = param_set.ParamGroup
class_name = param_set.ClassName
dialog_namespace = getattr(param_set, "DialogNameSpace", "Dialog")
namespace = f"{param_set.NameSpace}::{dialog_namespace}"
file_path = getattr(param_set, "FilePath", get_module_path(param_set))
param_file = getattr(param_set, "ParamSource", f"{file_path}/{class_name}.py")
header_file = getattr(param_set, "HeaderFile", f"{file_path}/{class_name}.h")
source_file = getattr(param_set, "SourceFile", f"{file_path}/{class_name}.cpp")
user_init = getattr(param_set, "UserInit", "")
headers = set()
if header:
cog.out(
f"""
{trace_comment()}
# include <QApplication>
# include <QLabel>
# include <QGroupBox>
# include <QGridLayout>
# include <QVBoxLayout>
# include <QHBoxLayout>
"""
)
for _, params in param_group:
for param in params:
for header in param.header_file:
if header not in headers:
headers.add(header)
cog.out(
f"""
#include <{header}>"""
)
cog.out(
f"""
{trace_comment()}
#include "{header_file}"
using namespace {namespace};
/* TRANSLATOR {namespace}::{class_name} */
"""
)
cog.out(
f"""
{trace_comment()}
{class_name}::{class_name}(QWidget* parent)
: PreferencePage( parent )
{{
"""
)
widgets_init(param_set)
cog.out(
f"""
{trace_comment()}
{user_init}
}}
"""
)
cog.out(
f"""
{trace_comment()}
{class_name}::~{class_name}()
{{
}}
"""
)
cog.out(
f"""
{trace_comment()}
void {class_name}::saveSettings()
{{"""
)
widgets_save(param_set)
cog.out(
f"""
}}
{trace_comment()}
void {class_name}::loadSettings()
{{"""
)
widgets_restore(param_set)
cog.out(
f"""
}}
{trace_comment()}
void {class_name}::retranslateUi()
{{
setWindowTitle(QObject::tr("{param_set.Title}"));"""
)
for title, params in param_group:
name = _regex.sub("", title)
cog.out(
f"""
group{name}->setTitle(QObject::tr("{title}"));"""
)
for row, param in enumerate(params):
param.retranslate()
cog.out(
f"""
}}
{trace_comment()}
void {class_name}::changeEvent(QEvent *e)
{{
if (e->type() == QEvent::LanguageChange) {{
retranslateUi();
}}
QWidget::changeEvent(e);
}}
"""
)
cog.out(
f"""
{trace_comment()}
#include "moc_{class_name}.cpp"
"""
)
_ParamPrefix = "User parameter:BaseApp/Preferences/"
class Param:
WidgetPrefix = ""
def __init__(self, name, default, doc="", title="", on_change=False, proxy=None, **kwd):
self.name = name
self.title = title if title else name
self._default = default
self._doc = doc
self.on_change = on_change
self.proxy = proxy
def _declare_label(self):
cog.out(
f"""
QLabel *label{self.name} = nullptr;"""
)
def declare_label(self):
if self.proxy:
self.proxy.declare_label(self)
else:
self._declare_label()
def _init_label(self, row, group_name):
cog.out(
f"""
label{self.name} = new QLabel(this);
layout{group_name}->addWidget(label{self.name}, {row}, 0);"""
)
def init_label(self, row, group_name):
if self.proxy:
self.proxy.init_label(self, row, group_name)
else:
self._init_label(row, group_name)
def _declare_widget(self):
self.declare_label()
cog.out(
f"""
{self.widget_type} *{self.widget_name} = nullptr;"""
)
def declare_widget(self):
if self.proxy:
self.proxy.declare_widget(self)
else:
self._declare_widget()
def _init_widget(self, row, group_name):
self.init_label(row, group_name)
cog.out(
f"""
{self.widget_name} = new {self.widget_type}(this);
layout{group_name}->addWidget({self.widget_name}, {row}, {self.widget_column});"""
)
if self.widget_setter:
cog.out(
f"""
{self.widget_name}->{self.widget_setter}({self.namespace}::{self.class_name}::default{self.name}());"""
)
self._init_pref_widget()
def _init_pref_widget(self):
cog.out(
f"""
{self.widget_name}->setEntryName("{self.name}");"""
)
if self.path.startswith(_ParamPrefix):
cog.out(
f"""
{self.widget_name}->setParamGrpPath("{self.path[len(_ParamPrefix):]}");"""
)
else:
cog.out(
f"""
{self.widget_name}->setParamGrpPath("{self.path}");"""
)
def init_widget(self, row, group_name):
if self.proxy:
self.proxy.init_widget(self, row, group_name)
else:
self._init_widget(row, group_name)
def _widget_save(self):
cog.out(
f"""
{self.widget_name}->onSave();"""
)
def widget_save(self):
if self.proxy:
self.proxy.widget_save(self)
else:
self._widget_save()
def _widget_restore(self):
cog.out(
f"""
{self.widget_name}->onRestore();"""
)
def widget_restore(self):
if self.proxy:
self.proxy.widget_restore(self)
else:
self._widget_restore()
def _retranslate_label(self):
cog.out(
f"""
label{self.name}->setText(QObject::tr("{self.title}"));
label{self.name}->setToolTip({self.widget_name}->toolTip());"""
)
def retranslate_label(self):
if self.proxy:
self.proxy.retranslate_label(self)
else:
self._retranslate_label()
def _retranslate(self):
cog.out(
f"""
{self.widget_name}->setToolTip(QApplication::translate("{self.class_name}", {self.namespace}::{self.class_name}::doc{self.name}()));"""
)
self.retranslate_label()
def retranslate(self):
if self.proxy:
self.proxy.retranslate(self)
else:
self._retranslate()
@property
def default(self):
return self._default
def doc(self, class_name):
if not self._doc:
return '""'
return f"""QT_TRANSLATE_NOOP("{class_name}",
{quote(self._doc)})"""
@property
def widget_type(self):
if self.proxy:
return self.proxy.widget_type(self)
return self.WidgetType
@property
def widget_prefix(self):
if self.proxy:
return self.proxy.widget_prefix(self)
return self.WidgetPrefix
@property
def widget_setter(self):
if self.proxy:
return self.proxy.widget_setter(self)
return self.WidgetSetter
@property
def widget_name(self):
return f"{self.widget_prefix}{self.name}"
@property
def widget_column(self):
return 1
def getter(self, handle):
return f'{handle}->Get{self.Type}("{self.name}", {self.default})'
def setter(self):
return f'instance()->handle->Set{self.Type}("{self.name}",v)'
class ParamBool(Param):
Type = "Bool"
C_Type = "bool"
WidgetType = "Gui::PrefCheckBox"
WidgetSetter = "setChecked"
@property
def default(self):
if isinstance(self._default, str):
return self._default
return "true" if self._default else "false"
def _declare_label(self):
pass
def _init_label(self, _row, _group_name):
pass
@property
def widget_column(self):
return 0
def _retranslate_label(self):
cog.out(
f"""
{self.widget_name}->setText(QObject::tr("{self.title}"));"""
)
class ParamFloat(Param):
Type = "Float"
C_Type = "double"
WidgetType = "Gui::PrefDoubleSpinBox"
WidgetSetter = "setValue"
class ParamString(Param):
Type = "ASCII"
C_Type = "std::string"
WidgetType = "Gui::PrefLineEdit"
WidgetSetter = "setText"
@property
def default(self):
return f'"{self._default}"'
class ParamQString(Param):
Type = "ASCII"
C_Type = "QString"
WidgetType = "Gui::PrefLineEdit"
WidgetSetter = "setText"
@property
def default(self):
return f'QStringLiteral("{self._default}")'
def getter(self, handle):
return (
f'QString::fromUtf8({handle}->Get{self.Type}("{self.name}", "{self._default}").c_str())'
)
def setter(self):
return f'instance()->handle->Set{self.Type}("{self.name}",v.toUtf8().constData())'
class ParamInt(Param):
Type = "Int"
C_Type = "long"
WidgetType = "Gui::PrefSpinBox"
WidgetSetter = "setValue"
class ParamUInt(Param):
Type = "Unsigned"
C_Type = "unsigned long"
WidgetType = "Gui::PrefSpinBox"
WidgetSetter = "setValue"
class ParamHex(ParamUInt):
@property
def default(self):
return "0x%08X" % self._default
class ParamProxy:
WidgetType = None
WidgetPrefix = ""
WidgetSetter = None
def __init__(self, param_bool=None):
self.param_bool = param_bool
def declare_label(self, param):
if not self.param_bool:
param._declare_label()
def widget_prefix(self, param):
return self.WidgetPrefix if self.WidgetPrefix else param.WidgetPrefix
def widget_type(self, param):
return self.WidgetType if self.WidgetType else param.WidgetType
def widget_setter(self, param):
return self.WidgetSetter if self.WidgetSetter else param.WidgetSetter
def declare_widget(self, param):
if self.param_bool:
self.param_bool.declare_widget()
param._declare_widget()
def init_label(self, param, row, group_name):
if not self.param_bool:
param._init_label(row, group_name)
def init_widget(self, param, row, group_name):
param._init_widget(row, group_name)
if self.param_bool:
self.param_bool.init_widget(row, group_name)
cog.out(
f"""
{param.widget_name}->setEnabled({self.param_bool.widget_name}->isChecked());
connect({self.param_bool.widget_name}, SIGNAL(toggled(bool)), {param.widget_name}, SLOT(setEnabled(bool)));"""
)
def retranslate_label(self, param):
if not self.param_bool:
param._retranslate_label()
def retranslate(self, param):
param._retranslate()
if self.param_bool:
self.param_bool.retranslate()
def widget_save(self, param):
param._widget_save()
if self.param_bool:
self.param_bool.widget_save()
def widget_restore(self, param):
param._widget_restore()
if self.param_bool:
self.param_bool.widget_restore()
class ComboBoxItem:
def __init__(self, text, tooltips=None, data=None):
self.text = text
self.tooltips = tooltips
self._data = data
@property
def data(self):
if self._data is None:
return "QVariant()"
if isinstance(self._data, str):
return f'QByteArray("{self._data}")'
return self._data
class ParamComboBox(ParamProxy):
WidgetType = "Gui::PrefComboBox"
def __init__(self, items, translate=True, param_bool=None):
super().__init__(param_bool)
self.translate = translate
self.items = []
for item in items:
if isinstance(item, str):
item = ComboBoxItem(item)
elif isinstance(item, tuple):
item = ComboBoxItem(*item)
else:
assert isinstance(item, ComboBoxItem)
self.items.append(item)
def widget_setter(self, _param):
return None
def init_widget(self, param, row, group_name):
super().init_widget(param, row, group_name)
if self.translate:
cog.out(
f"""
for (int i=0; i<{len(self.items)}; ++i) {trace_comment()}
{param.widget_name}->addItem(QString());"""
)
for i, item in enumerate(self.items):
if not self.translate:
cog.out(
f"""
{param.widget_name}->addItem(QStringLiteral("{item.text}"));"""
)
if item._data is not None:
cog.out(
f"""
{param.widget_name}->setItemData({param.widget_name}->count()-1, {item.data});"""
)
cog.out(
f"""
{param.widget_name}->setCurrentIndex({param.namespace}::{param.class_name}::default{param.name}());"""
)
def retranslate(self, param):
super().retranslate(param)
cog.out(
f"""
{trace_comment()}"""
)
for i, item in enumerate(self.items):
if self.translate:
cog.out(
f"""
{param.widget_name}->setItemText({i}, QObject::tr("{item.text}"));"""
)
if item.tooltips:
cog.out(
f"""
{param.widget_name}->setItemData({i}, QObject::tr("{item.tooltips}"), Qt::ToolTipRole);"""
)
class ParamLinePattern(ParamProxy):
WidgetType = "Gui::PrefLinePattern"
def widget_setter(self, _param):
return None
def init_widget(self, param, row, group_name):
super().init_widget(param, row, group_name)
cog.out(
f"""
{trace_comment()}
for (int i=1; i<{param.widget_name}->count(); ++i) {{
if ({param.widget_name}->itemData(i).toInt() == {param.default})
{param.widget_name}->setCurrentIndex(i);
}}"""
)
class ParamColor(ParamProxy):
WidgetType = "Gui::PrefColorButton"
WidgetSetter = "setPackedColor"
def __init__(self, param_bool=None, transparency=True):
super().__init__(param_bool)
self.transparency = transparency
def init_widget(self, param, row, group_name):
super().init_widget(param, row, group_name)
if self.transparency:
cog.out(
f"""
{param.widget_name}->setAllowTransparency(true);"""
)
class ParamFile(ParamProxy):
WidgetType = "Gui::PrefFileChooser"
WidgetSetter = "setFileNameStd"
class ParamSpinBox(ParamProxy):
def __init__(self, value_min, value_max, value_step, decimals=0, param_bool=None, suffix=""):
super().__init__(param_bool)
self.value_min = value_min
self.value_max = value_max
self.value_step = value_step
self.decimals = decimals
self.suffix = suffix
def init_widget(self, param, row, group_name):
super().init_widget(param, row, group_name)
cog.out(
f"""
{trace_comment()}
{param.widget_name}->setMinimum({self.value_min});
{param.widget_name}->setMaximum({self.value_max});
{param.widget_name}->setSingleStep({self.value_step});
{param.widget_name}->setAlignment(Qt::AlignRight);"""
)
if self.decimals:
cog.out(
f"""
{param.widget_name}->setDecimals({self.decimals});"""
)
if self.suffix:
cog.out(
f"""
{param.widget_name}->setSuffix(QLatin1String("{self.suffix}"));"""
)
class ParamShortcutEdit(ParamProxy):
WidgetType = "Gui::PrefAccelLineEdit"
WidgetSetter = "setDisplayText"
class Property:
def __init__(self, name, property_type, doc, group=None, prop_flags=None, static=False):
self.name = name
self.type_name = property_type
self.doc = doc
self.prop_flags = prop_flags if prop_flags else "App::Prop_None"
self.static = static
self.group = group if group else ""
def declare(self):
if self.static:
cog.out(
f"""
static {self.type_name} *get{self.name}Property(App::DocumentObject *obj, bool force=false);
inline {self.type_name} *get{self.name}Property(bool force=false) {{
return get{self.name}Property(this, force);
}}"""
)
else:
cog.out(
f"""
{self.type_name} *get{self.name}Property(bool force=false);"""
)
def define(self, class_name):
if self.static:
cog.out(
f"""
{trace_comment()}
{self.type_name} *{class_name}::get{self.name}Property(App::DocumentObject *obj, bool force)
{{"""
)
else:
cog.out(
f"""
{trace_comment()}
{self.type_name} *{class_name}::get{self.name}Property(bool force)
{{
auto obj = this;"""
)
cog.out(
f"""
if (auto prop = Base::freecad_dynamic_cast<{self.type_name}>(
obj->getPropertyByName("{self.name}")))
{{
if (prop->getContainer() == obj)
return prop;
}}
if (!force)
return nullptr;
return static_cast<{self.type_name}*>(obj->addDynamicProperty(
"{self.type_name}", "{self.name}", "{self.group}",
{quote(self.doc)},
{self.prop_flags}));
}}
"""
)
def declare_properties(properties):
cog.out(
f"""
{trace_comment()}"""
)
for prop in properties:
prop.declare()
def define_properties(properties, class_name):
for prop in properties:
prop.define(class_name)