Download src/Tools/params_utils.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 31.6 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Tools/params_utils.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Tools/params_utils.py
-
curl -L -o params_utils.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Tools/params_utils.py
31.6 kB
| # 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() | |
| 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)})""" | |
| def widget_type(self): | |
| if self.proxy: | |
| return self.proxy.widget_type(self) | |
| return self.WidgetType | |
| def widget_prefix(self): | |
| if self.proxy: | |
| return self.proxy.widget_prefix(self) | |
| return self.WidgetPrefix | |
| def widget_setter(self): | |
| if self.proxy: | |
| return self.proxy.widget_setter(self) | |
| return self.WidgetSetter | |
| def widget_name(self): | |
| return f"{self.widget_prefix}{self.name}" | |
| 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" | |
| 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 | |
| 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" | |
| def default(self): | |
| return f'"{self._default}"' | |
| class ParamQString(Param): | |
| Type = "ASCII" | |
| C_Type = "QString" | |
| WidgetType = "Gui::PrefLineEdit" | |
| WidgetSetter = "setText" | |
| 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): | |
| 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 | |
| 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) | |