Klayout PyCell integration

-added tl::optional as derivate of std::optional for c++17 and above, reduced
 implementation otherwise
-fixed missing include for c++17 and above
-added range constraints for PCell parameter

Signed-off-by: ThomasZecha <zecha@ihp-microelectronics.com>
This commit is contained in:
ThomasZecha 2024-03-06 08:42:38 +01:00
parent ef53700baa
commit eea4344976
9 changed files with 376 additions and 17 deletions

View File

@ -30,6 +30,7 @@
#include "dbLayout.h" #include "dbLayout.h"
#include "tlVariant.h" #include "tlVariant.h"
#include "tlObject.h" #include "tlObject.h"
#include "tlOptional.h"
namespace db namespace db
{ {
@ -69,7 +70,7 @@ public:
* @brief The default constructor * @brief The default constructor
*/ */
PCellParameterDeclaration () PCellParameterDeclaration ()
: m_hidden (false), m_readonly (false), m_type (t_none) : m_hidden (false), m_readonly (false), m_type (t_none), m_range()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -78,7 +79,7 @@ public:
* @brief The constructor with a name * @brief The constructor with a name
*/ */
PCellParameterDeclaration (const std::string &name) PCellParameterDeclaration (const std::string &name)
: m_hidden (false), m_readonly (false), m_type (t_none), m_name (name) : m_hidden (false), m_readonly (false), m_type (t_none), m_name (name), m_range()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -87,7 +88,7 @@ public:
* @brief The constructor with a name, type and description * @brief The constructor with a name, type and description
*/ */
PCellParameterDeclaration (const std::string &name, type t, const std::string &description) PCellParameterDeclaration (const std::string &name, type t, const std::string &description)
: m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description) : m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_range()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -96,7 +97,7 @@ public:
* @brief The constructor with a name, type, description and default value * @brief The constructor with a name, type, description and default value
*/ */
PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const tl::Variant &def) PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const tl::Variant &def)
: m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description) : m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_range()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -105,7 +106,7 @@ public:
* @brief The constructor with a name, type, description, default value and unit * @brief The constructor with a name, type, description, default value and unit
*/ */
PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const tl::Variant &def, const std::string &unit) PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const tl::Variant &def, const std::string &unit)
: m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_unit (unit) : m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_unit (unit), m_range()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -114,7 +115,7 @@ public:
* @brief The constructor with a name, type and description and choice values / choice descriptions * @brief The constructor with a name, type and description and choice values / choice descriptions
*/ */
PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const std::vector<tl::Variant> &choices, const std::vector<std::string> &choice_descriptions) PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const std::vector<tl::Variant> &choices, const std::vector<std::string> &choice_descriptions)
: m_choices (choices), m_choice_descriptions (choice_descriptions), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description) : m_choices (choices), m_choice_descriptions (choice_descriptions), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_range()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -248,6 +249,53 @@ public:
} }
/** /**
* @brief A enum describing the action of range violated
*/
enum Action {
t_Reject = 1, // reject the parameter
t_Accept, // accept the parameter
t_Use_Default // use a default parameter (currently not supported)
};
void set_range (const tl::Variant& low, const tl::Variant& high, const tl::Variant& resolution, Action action = t_Reject)
{
m_range = Range(low, high, resolution, action);
}
typedef struct _Range {
_Range() :
m_low(),
m_high(),
m_resolution(),
m_action(t_Reject) {}
_Range(const tl::Variant& low, const tl::Variant& high, const tl::Variant& resolution, Action action = t_Reject) :
m_low(low),
m_high(high),
m_resolution(resolution),
m_action(action) {}
bool operator== (const _Range& other) const
{
return
m_low == other.m_low &&
m_high == other.m_high &&
m_resolution == other.m_resolution &&
m_action == other.m_action;
}
tl::Variant m_low;
tl::Variant m_high;
tl::Variant m_resolution;
Action m_action;
} Range;
const tl::optional<Range>& get_range() const
{
return m_range;
}
/**
* @brief Getter for the choice descriptions * @brief Getter for the choice descriptions
* *
* The choice descriptions correspond to choice values. The descriptions * The choice descriptions correspond to choice values. The descriptions
@ -280,7 +328,8 @@ public:
m_type == d.m_type && m_type == d.m_type &&
m_name == d.m_name && m_name == d.m_name &&
m_description == d.m_description && m_description == d.m_description &&
m_unit == d.m_unit; m_unit == d.m_unit &&
m_range == d.m_range;
} }
private: private:
@ -291,6 +340,7 @@ private:
type m_type; type m_type;
std::string m_name; std::string m_name;
std::string m_description, m_unit; std::string m_description, m_unit;
tl::optional<Range> m_range;
}; };
/** /**

View File

@ -29,6 +29,7 @@
#include "dbPCellDeclaration.h" #include "dbPCellDeclaration.h"
#include "dbLibrary.h" #include "dbLibrary.h"
#include "dbLibraryManager.h" #include "dbLibraryManager.h"
#include "tlLog.h"
namespace gsi namespace gsi
{ {
@ -719,12 +720,41 @@ void clear_choices (db::PCellParameterDeclaration *pd)
void add_choice (db::PCellParameterDeclaration *pd, const std::string &d, const tl::Variant &v) void add_choice (db::PCellParameterDeclaration *pd, const std::string &d, const tl::Variant &v)
{ {
std::vector<tl::Variant> vv = pd->get_choices (); if (!pd->get_range().has_value())
std::vector<std::string> dd = pd->get_choice_descriptions (); {
vv.push_back (v); std::vector<tl::Variant> vv = pd->get_choices ();
dd.push_back (d); std::vector<std::string> dd = pd->get_choice_descriptions ();
pd->set_choice_descriptions (dd); vv.push_back (v);
pd->set_choices (vv); dd.push_back (d);
pd->set_choice_descriptions (dd);
pd->set_choices (vv);
}
else
{
tl::warn
<< "PCell parameter '" << pd->get_name() << "' has a range constraint, could not add choice '"
<< v.to_string() << "'";
}
}
void set_range (db::PCellParameterDeclaration *pd, const tl::Variant& low, const tl::Variant& high, const tl::Variant& resolution, unsigned int action)
{
if (pd->get_choices().empty() && pd->get_choice_descriptions().empty())
{
if ((pd->get_type() == db::PCellParameterDeclaration::t_int
|| pd->get_type() == db::PCellParameterDeclaration::t_double) &&
!low.is_nil() && low.can_convert_to_double() &&
!high.is_nil() && high.can_convert_to_double())
{
pd->set_range(low, high, resolution, db::PCellParameterDeclaration::Action(action));
}
}
else
{
tl::warn
<< "PCell parameter '" << pd->get_name() << "' has a choice constraint, could not add range '["
<< low.to_string() << ", " << high.to_string() << "]'";
}
} }
static unsigned int pd_type_int () static unsigned int pd_type_int ()
@ -772,6 +802,21 @@ static unsigned int pd_type_none ()
return (unsigned int) db::PCellParameterDeclaration::t_none; return (unsigned int) db::PCellParameterDeclaration::t_none;
} }
static unsigned int pd_action_reject ()
{
return (unsigned int) db::PCellParameterDeclaration::t_Reject;
}
static unsigned int pd_action_accept ()
{
return (unsigned int) db::PCellParameterDeclaration::t_Accept;
}
static unsigned int pd_action_use_default ()
{
return (unsigned int) db::PCellParameterDeclaration::t_Use_Default;
}
db::PCellParameterDeclaration *ctor_pcell_parameter (const std::string &name, unsigned int type, const std::string &description) db::PCellParameterDeclaration *ctor_pcell_parameter (const std::string &name, unsigned int type, const std::string &description)
{ {
db::PCellParameterDeclaration *pd = new db::PCellParameterDeclaration (); db::PCellParameterDeclaration *pd = new db::PCellParameterDeclaration ();
@ -874,6 +919,19 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
"This method will add the given value with the given description to the list of\n" "This method will add the given value with the given description to the list of\n"
"choices. If choices are defined, KLayout will show a drop-down box instead of an\n" "choices. If choices are defined, KLayout will show a drop-down box instead of an\n"
"entry field in the parameter user interface.\n" "entry field in the parameter user interface.\n"
"If a range is already set for this parameter the choice will not be added and a warning message is showed.\n"
) +
gsi::method_ext ("set_range", &set_range, gsi::arg ("low"), gsi::arg ("high"), gsi::arg ("resolution"), gsi::arg ("action"),
"@brief Set a range constraint\n"
"This method will set a range constraint to the parameter with 'low' and 'high' as minimum and maximum value.\n"
"This range constraint will only be set if the parameter-, the low- and the high-type are numeric.\n"
"If a choice is already set for this parameter the range will not be set and a warning message is showed.\n"
"The optional parameter 'resolution' will give a desired resolution value (currently not used).\n"
"The optional parameter 'action' determines the action to be invoked.\n"
"This action can be one of three values: REJECT, ACCEPT, USE_DEFAULT. If this failure action\n"
"parameter is not specified, then it will be REJECT by default.\n"
"If a range constraint is violated this parameter is marked wrong with violation hint in the\n"
"parameter user interface.\n"
) + ) +
gsi::method ("choice_values", &db::PCellParameterDeclaration::get_choices, gsi::method ("choice_values", &db::PCellParameterDeclaration::get_choices,
"@brief Returns a list of choice values\n" "@brief Returns a list of choice values\n"
@ -897,7 +955,10 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
gsi::method ("TypeLayer", &pd_type_layer, "@brief Type code: a layer (a \\LayerInfo object)") + gsi::method ("TypeLayer", &pd_type_layer, "@brief Type code: a layer (a \\LayerInfo object)") +
gsi::method ("TypeShape", &pd_type_shape, "@brief Type code: a guiding shape (Box, Edge, Point, Polygon or Path)") + gsi::method ("TypeShape", &pd_type_shape, "@brief Type code: a guiding shape (Box, Edge, Point, Polygon or Path)") +
gsi::method ("TypeCallback", &pd_type_callback, "@brief Type code: a button triggering a callback\n\nThis code has been introduced in version 0.28.") + gsi::method ("TypeCallback", &pd_type_callback, "@brief Type code: a button triggering a callback\n\nThis code has been introduced in version 0.28.") +
gsi::method ("TypeNone", &pd_type_none, "@brief Type code: unspecific type") gsi::method ("TypeNone", &pd_type_none, "@brief Type code: unspecific type") +
gsi::method ("REJECT", &pd_action_reject, "@brief Action type: reject violating parameter") +
gsi::method ("ACCEPT", &pd_action_accept, "@brief Action type: accept violating parameter") +
gsi::method ("USE_DEFAULT", &pd_action_use_default, "@brief Action type: use default instead violating parameter (currently not supported)")
, ,
"@brief A PCell parameter declaration\n" "@brief A PCell parameter declaration\n"
"\n" "\n"

View File

@ -398,6 +398,18 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P
inner_grid->addWidget (icon_label, row, 0); inner_grid->addWidget (icon_label, row, 0);
m_icon_widgets.push_back (icon_label); m_icon_widgets.push_back (icon_label);
m_all_widgets.back ().push_back (icon_label); m_all_widgets.back ().push_back (icon_label);
std::string range;
if (p->get_range().has_value())
{
const tl::Variant& low(p->get_range().value().m_low);
const tl::Variant& high(p->get_range().value().m_high);
range = tl::sprintf(
" [%s, %s]" ,
low.is_nil() ? "-\u221e" : low.to_string(),
high.is_nil() ? "\u221e" : high.to_string());
}
if (p->get_type () != db::PCellParameterDeclaration::t_callback) { if (p->get_type () != db::PCellParameterDeclaration::t_callback) {
@ -406,7 +418,8 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P
leader = tl::sprintf ("[%s] ", p->get_name ()); leader = tl::sprintf ("[%s] ", p->get_name ());
} }
QLabel *l = new QLabel (tl::to_qstring (leader + description), inner_frame); QLabel *l = new QLabel (tl::to_qstring (leader + description + range), inner_frame);
inner_grid->addWidget (l, row, 1); inner_grid->addWidget (l, row, 1);
m_all_widgets.back ().push_back (l); m_all_widgets.back ().push_back (l);
@ -762,6 +775,11 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool
ps.set_value (tl::Variant (v)); ps.set_value (tl::Variant (v));
lay::indicate_error (le, (tl::Exception *) 0); lay::indicate_error (le, (tl::Exception *) 0);
if (p->get_range().has_value())
{
check_range(tl::Variant(v), p->get_range().value());
}
} catch (tl::Exception &ex) { } catch (tl::Exception &ex) {
lay::indicate_error (le, &ex); lay::indicate_error (le, &ex);
@ -786,6 +804,11 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool
ps.set_value (tl::Variant (v)); ps.set_value (tl::Variant (v));
lay::indicate_error (le, (tl::Exception *) 0); lay::indicate_error (le, (tl::Exception *) 0);
if (p->get_range().has_value())
{
check_range(tl::Variant(v), p->get_range().value());
}
} catch (tl::Exception &ex) { } catch (tl::Exception &ex) {
lay::indicate_error (le, &ex); lay::indicate_error (le, &ex);
@ -1085,6 +1108,23 @@ PCellParametersPage::states_from_parameters (db::ParameterStates &states, const
} }
} }
void
PCellParametersPage::check_range(const tl::Variant& value, const db::PCellParameterDeclaration::Range& range)
{
if (db::PCellParameterDeclaration::Action(range.m_action) == db::PCellParameterDeclaration::t_Reject)
{
if (!range.m_low.is_nil() && value < range.m_low)
{
throw tl::Exception(tl::to_string (tr("Range violation: value < low")));
}
if (!range.m_high.is_nil() && range.m_high < value)
{
throw tl::Exception(tl::to_string (tr("Range violation: value > high")));
}
}
}
} }
#endif #endif

View File

@ -181,6 +181,7 @@ private:
void get_parameters_internal (db::ParameterStates &states, bool &edit_error); void get_parameters_internal (db::ParameterStates &states, bool &edit_error);
std::vector<tl::Variant> parameter_from_states (const db::ParameterStates &states) const; std::vector<tl::Variant> parameter_from_states (const db::ParameterStates &states) const;
void states_from_parameters (db::ParameterStates &states, const std::vector<tl::Variant> &parameters); void states_from_parameters (db::ParameterStates &states, const std::vector<tl::Variant> &parameters);
void check_range(const tl::Variant& value, const db::PCellParameterDeclaration::Range& range);
}; };
} }

View File

@ -35,6 +35,7 @@
#include <set> #include <set>
#include <stdexcept> #include <stdexcept>
#include <cstdint> #include <cstdint>
#include <optional>
#if defined(HAVE_QT) #if defined(HAVE_QT)
#include <QString> #include <QString>

View File

@ -36569,6 +36569,18 @@ class PCellParameterDeclaration:
r""" r"""
@brief Type code: string data @brief Type code: string data
""" """
REJECT: ClassVar[int]
r"""
@brief Action type: reject violating parameter
"""
ACCEPT: ClassVar[int]
r"""
@brief Action type: accept violating parameter
"""
USE_DEFAULT: ClassVar[int]
r"""
@brief Action type: use default instead violating parameter (currently not supported)
"""
default: Any default: Any
r""" r"""
Getter: Getter:

View File

@ -55,7 +55,8 @@ SOURCES = \
tlEquivalenceClusters.cc \ tlEquivalenceClusters.cc \
tlUniqueName.cc \ tlUniqueName.cc \
tlRecipe.cc \ tlRecipe.cc \
tlEnv.cc tlEnv.cc \
tlOptional.cc
HEADERS = \ HEADERS = \
tlAlgorithm.h \ tlAlgorithm.h \
@ -121,7 +122,8 @@ HEADERS = \
tlUniqueName.h \ tlUniqueName.h \
tlRecipe.h \ tlRecipe.h \
tlSelect.h \ tlSelect.h \
tlEnv.h tlEnv.h \
tlOptional.h
equals(HAVE_GIT2, "1") { equals(HAVE_GIT2, "1") {

32
src/tl/tl/tlOptional.cc Normal file
View File

@ -0,0 +1,32 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlOptional.h"
namespace tl
{
#if __cplusplus < 201703L
extern const nullopt_t nullopt = nullopt_t();
#endif
} // namespace tl

160
src/tl/tl/tlOptional.h Normal file
View File

@ -0,0 +1,160 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlOptional
#define HDR_tlOptional
#include "tlAssert.h"
#include <iostream>
#include <optional>
namespace tl
{
#if __cplusplus >= 201703L
template<typename T>
class optional : public std::optional<T> {};
#else
struct nullopt_t {};
extern const nullopt_t nullopt;
/*
* Poor man's partly implementation of C++17's std::optional
*/
template<typename T>
class optional
{
public:
optional() :
a_value(),
a_isValid(false)
{}
optional(const nullopt_t&) :
a_value(),
a_isValid(false)
{}
optional(const T &value) :
a_value(value),
a_isValid(true)
{}
void reset()
{
a_isValid = false;
}
bool has_value() const { return a_isValid; }
T &value()
{
tl_assert(a_isValid);
return a_value;
}
const T &value() const
{
tl_assert(a_isValid);
return a_value;
}
T& operator* ()
{
return value();
}
const T& operator* () const
{
return value();
}
T* operator-> ()
{
return &value();
}
const T* operator-> () const
{
return &value();
}
private:
T a_value;
bool a_isValid;
};
template<typename T>
optional<T> make_optional(const T& value)
{
return optional<T>(value);
}
template<typename T>
bool operator==(const optional<T> &lhs, const optional<T> &rhs)
{
if (lhs.has_value() != rhs.has_value())
{
return false;
}
if (!lhs.has_value())
{
return true;
}
return lhs.value() == rhs.value();
}
template<typename T>
bool operator!=(const optional<T> &lhs, const optional<T> &rhs)
{
return !(lhs == rhs);
}
template<typename T>
std::ostream &operator<<(std::ostream &ostr, const optional<T> &rhs)
{
if (rhs.has_value())
{
ostr << rhs.value();
}
else
{
ostr << "<invalid>";
}
return ostr;
}
#endif /* __cplusplus >= 201703L */
} // namespace tl
#endif /* HDR_tlOptional */