* Removed outdated UTF8 declaration * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1227 lines
31 KiB
Python
1227 lines
31 KiB
Python
# ***************************************************************************
|
|
# * 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 <boost/signals2.hpp>" 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 boost::signals2::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()}
|
|
boost::signals2::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()}
|
|
boost::signals2::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 = freecad_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)
|