Merge pull request #923 from KLayout/usablity-enhancements

Usablity enhancements
This commit is contained in:
Matthias Köfferlein 2021-10-20 22:26:38 +02:00 committed by GitHub
commit 7e6b4662ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 205 additions and 47 deletions

View File

@ -180,7 +180,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
<< tl::arg ("input_b", &infile_b, "The second input file (any format, may be gzip compressed)")
<< tl::arg ("?output", &output, "The output file to which the XOR differences are written",
"This argument is optional. If not given, the exit status alone will indicate whether the layouts "
"are identical or not."
"are identical or not. The output is a layout file. The format of the file is derived "
"from the file name's suffix (.oas[.gz] for (gzipped) OASIS, .gds[.gz] for (gzipped) GDS2 etc.)."
)
<< tl::arg ("-ta|--top-a=name", &top_a, "Specifies the top cell for the first layout",
"Use this option to take a specific cell as the top cell from the first layout. All "
@ -248,6 +249,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
db::Layout layout_b;
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (A): ")) + infile_a);
db::LoadLayoutOptions load_options;
generic_reader_options_a.configure (load_options);
@ -257,6 +260,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
}
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (B): ")) + infile_b);
db::LoadLayoutOptions load_options;
generic_reader_options_b.configure (load_options);

View File

@ -2115,7 +2115,7 @@ MainService::cm_tap ()
QPoint mp = view ()->view_object_widget ()->mapToGlobal (view ()->view_object_widget ()->mouse_position ());
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->source (true).to_string ()));
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->display_string (view (), true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}

View File

@ -24,9 +24,9 @@
<topic href="/manual/view_state.xml"/>
<topic href="/manual/bookmarks.xml"/>
<topic href="/manual/descend.xml"/>
<topic href="/manual/layer_views.xml"/>
<topic href="/manual/layer_color.xml"/>
<topic href="/manual/layer_content.xml"/>
<topic href="/manual/layer_content.xml"/>
<topic href="/manual/line_style.xml"/>
<topic href="/manual/layer_animation.xml"/>
<topic href="/manual/layer_style.xml"/>

View File

@ -100,5 +100,9 @@
a selector as well, only those shapes will be shown that meet both criteria.
</p>
<p>
A general description for the source notation is found here: <link href="/about/layer_sources.xml"/>.
</p>
</doc>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<doc>
<title>The Layer List (Layer Views)</title>
<keyword name="Layer list"/>
<keyword name="Layer views"/>
<p>
An important concept in KLayout are the layer views. KLayout displays the layers
of a layout by default in a list on the right side of the main window.
This list however, does not directly reflect the layers in the layout database.
Instead this list is a collection of "views". A view is a description of what is
to be displayed and how.
</p>
<p>
Essentially, the entries in the layer list are pointers to layers in the
database, together with a description how to paint the shapes on these layers (the
"layer properties").
</p>
<p>
The pointer is the "source" of a layer view. This is typically a GDS layer and datatype,
but can be a layer name (for DXF for example). There are also abstract sources (such as
cell boundaries) and the sources can include selectors or modifiers. Selectors are
used to only display shapes with certain user properties or from certain hierarchy
levels. Modifiers transform the shapes before they are drawn for example.
The source is defined by a "source specification" - this is a string describing the
database layer and selectors and modifiers. A simple source string is "1/0" which is
for GDS layer 1, datatype 0 without and selectors or modifiers.
To change the source, use "Change Source" from the layer list's context menu.
</p>
<p>
See <link href="/manual/layer_source_expert.xml"/> for some source specification string applications
and more details.
</p>
<p>
Beside the source, a layer entry has a display name. This is an arbitrary text
providing a description for the user. By default - when no such name is present -
the source of the layer will be displayed.
To change the display name, use "Rename" from the layer list's context menu.
</p>
<p>
Plus of course, the layer views have many options to specify the drawing style,
animations and decorations.
</p>
<p>
The concept of separating views from the database layers opens some interesting options:
</p>
<ul>
<li>Layer views can refer to individual layouts from multi-layout views (through the "@1", "@2", ... notation in the source).
Hence, multiple layouts can be mixed in a single layer list.</li>
<li>Layers can be present in the list which do not need to be present in the database. Such a layer is shown as empty.
This is important as in GDS an empty layer is equivalent to non-existing. Still you may want to have it shown in the
layer list - the views offer this option.</li>
<li>Vice versa, database layer may not be listed in the layer list if no corresponding layer view is present. This
way, auxiliary or debug layers can be omitted from the layer list. A "wildcard specification" is available to make
sure, all layers are shown if you need to see all.</li>
<li>Multiple tabs can be present to provide multiple views on the same layouts. This is just an alternative set of
layer views.</li>
<li>Layer grouping, sorting etc. are just operations on the views, no database change is involved.</li>
</ul>
<p>
The concept on the other hand is slightly counter-intuitive at first.
Here are some hints:
</p>
<ul>
<li>Renaming a layer does not change the source - if you rename a layer to something like "1/0", you are likely to fool yourself thinking this is layer 1, datatype 0.</li>
<li>Changing a layer view's source does not change the database too - it will just change the pointer. To change a layer's information in the database, use Edit/Layer/Edit Layer Specification.</li>
<li>Deleting a layer from the layer list does <b>not delete</b> the layer from the database. Use Edit/Layer/Delete Layer instead.</li>
<li>Additing a new layer does not immediately create the layer in the database. Only once you draw something on that layer, it is generated in the database.</li>
</ul>
</doc>

View File

@ -158,6 +158,7 @@
<file alias="landmarks.xml">doc/manual/landmarks.xml</file>
<file alias="layer_animation.xml">doc/manual/layer_animation.xml</file>
<file alias="layer_boolean.xml">doc/manual/layer_boolean.xml</file>
<file alias="layer_views.xml">doc/manual/layer_views.xml</file>
<file alias="layer_color.xml">doc/manual/layer_color.xml</file>
<file alias="layer_content.xml">doc/manual/layer_content.xml</file>
<file alias="layer_fill.xml">doc/manual/layer_fill.xml</file>

View File

@ -2025,44 +2025,47 @@ MainWindow::cm_load_layer_props ()
if (current_view ()) {
std::string fn;
if (mp_lprops_fdia->get_open (fn, tl::to_string (QObject::tr ("Load Layer Properties File")))) {
int target_cv_index = -2;
if (current_view ()->cellviews () > 1 && is_single_cv_layer_properties_file (fn)) {
QStringList items;
items << QString (QObject::tr ("Take it as it is"));
items << QString (QObject::tr ("Apply to all layouts"));
for (unsigned int i = 0; i < current_view ()->cellviews (); ++i) {
items << QString (tl::to_qstring (tl::to_string (QObject::tr ("Apply to ")) + current_view ()->cellview (i)->name () + " (@" + tl::to_string (i + 1) + ")"));
}
bool ok;
QString item = QInputDialog::getItem(this, QObject::tr ("Apply Layer Properties File"),
QObject::tr ("There are multiple layouts in that panel but the layer properties file contains properties for a single one.\nWhat should be done?"),
items, 1, false, &ok);
if (!ok || item.isEmpty()) {
return;
}
target_cv_index = items.indexOf (item) - 2;
}
if (target_cv_index > -2) {
load_layer_properties (fn, target_cv_index, false /*current view only*/, false /*don't add default*/);
} else {
load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
}
load_layer_props_from_file (fn);
add_to_other_mru (fn, cfg_mru_layer_properties);
}
} else {
throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the layer properties for")));
}
}
void
MainWindow::load_layer_props_from_file (const std::string &fn)
{
int target_cv_index = -2;
if (current_view ()->cellviews () > 1 && is_single_cv_layer_properties_file (fn)) {
QStringList items;
items << QString (QObject::tr ("Take it as it is"));
items << QString (QObject::tr ("Apply to all layouts"));
for (unsigned int i = 0; i < current_view ()->cellviews (); ++i) {
items << QString (tl::to_qstring (tl::to_string (QObject::tr ("Apply to ")) + current_view ()->cellview (i)->name () + " (@" + tl::to_string (i + 1) + ")"));
}
bool ok;
QString item = QInputDialog::getItem(this, QObject::tr ("Apply Layer Properties File"),
QObject::tr ("There are multiple layouts in that panel but the layer properties file contains properties for a single one.\nWhat should be done?"),
items, 1, false, &ok);
if (!ok || item.isEmpty()) {
return;
}
target_cv_index = items.indexOf (item) - 2;
}
if (target_cv_index > -2) {
load_layer_properties (fn, target_cv_index, false /*current view only*/, false /*don't add default*/);
} else {
load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
}
}
void
MainWindow::save_session (const std::string &fn)
{
@ -3238,7 +3241,7 @@ MainWindow::open_recent_layer_properties (size_t n)
if (n < m_mru_layer_properties.size ()) {
std::string fn = m_mru_layer_properties [n];
load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
load_layer_props_from_file (fn);
add_to_other_mru (fn, cfg_mru_layer_properties); // make it the latest
}

View File

@ -805,6 +805,7 @@ private:
int dirty_files (std::string &dirty_files);
void load_layer_props_from_file (const std::string &fn);
void interactive_close_view (int index, bool all_cellviews);
void call_on_current_view (void (lay::LayoutView::*func) (), const std::string &op_desc);
void current_view_changed ();

View File

@ -38,6 +38,7 @@
#include "tlLog.h"
#include "tlProgress.h"
#include "tlTimer.h"
#include "rdbUtils.h"
#include <QInputDialog>
#include <QHeaderView>
@ -537,10 +538,10 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu)
rdb::Item *item = rdb.create_item (cell->id (), cat->id ());
if (! v->is_list ()) {
item->add_value (std::string (v->to_string ()));
rdb::add_item_value (item, *v, dbu);
} else {
for (std::vector<tl::Variant>::const_iterator i = v->get_list ().begin (); i != v->get_list ().end (); ++i) {
item->add_value (std::string (i->to_string ()));
rdb::add_item_value (item, *i, dbu);
}
}

View File

@ -690,6 +690,11 @@ Class<lay::LayoutView> decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
"\n"
"In 0.25, this method has been moved from MainWindow to LayoutView.\n"
) +
gsi::method ("is_editable?", &lay::LayoutView::is_editable,
"@brief Returns true if the view is in editable mode\n"
"\n"
"This read-only attribute has been added in version 0.27.5.\n"
) +
gsi::method ("reload_layout", &lay::LayoutView::reload_layout, gsi::arg ("cv"),
"@brief Reloads the given cellview\n"
"\n"

View File

@ -203,7 +203,7 @@ D25View::activated ()
bool any = mp_ui->d25_view->attach_view (view ());
if (! any) {
mp_ui->d25_view->attach_view (0);
throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in the view.\nUse \"Tools/Manage Technologies\" to set up a z stack.")));
throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in this view.\nUse \"Tools/Manage Technologies\" to set up a z stack or check if it applies to the layers here.")));
}
mp_ui->d25_view->reset ();

View File

@ -311,13 +311,13 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
// Move "into" or "out"
double d = (event->key () == Qt::Key_Up ? 0.1 : -0.1);
double d = (event->key () == Qt::Key_Up ? 0.05 : -0.05);
QMatrix4x4 t;
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
QVector3D cd = t.inverted ().map (QVector3D (0, 0, cam_dist ()));
set_displacement (displacement () + d * cd);
set_displacement (displacement () + d * cd / m_scale_factor);
}
@ -327,7 +327,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
// Ctrl + left/right changes azimuths
double d = (event->key () == Qt::Key_Right ? 2 : -2);
double d = (event->key () == Qt::Key_Right ? 1 : -1);
double a = cam_azimuth () + d;
if (a < -180.0) {
@ -348,7 +348,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
QVector3D cd = t.inverted ().map (QVector3D (cam_dist (), 0, 0));
set_displacement (displacement () + d * cd);
set_displacement (displacement () + d * cd / m_scale_factor);
}
@ -634,11 +634,6 @@ D25ViewWidget::prepare_view ()
}
m_bbox &= cell_bbox;
if (m_bbox.empty ()) {
return false;
}
bool any = false;
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));

View File

@ -317,5 +317,52 @@ void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb:
}
}
void add_item_value (rdb::Item *item, const tl::Variant &v, double dbu)
{
if (dbu > 0 && v.is_user<db::Box> ()) {
item->add_value (db::CplxTrans (dbu) * v.to_user<db::Box> ());
} else if (dbu > 0 && v.is_user<db::Point> ()) {
db::DPoint p = db::CplxTrans (dbu) * v.to_user<db::Point> ();
item->add_value (db::DEdge (p, p));
} else if (dbu > 0 && v.is_user<db::Polygon> ()) {
item->add_value (db::CplxTrans (dbu) * v.to_user<db::Polygon> ());
} else if (dbu > 0 && v.is_user<db::SimplePolygon> ()) {
db::DPolygon p;
db::DSimplePolygon sp = db::CplxTrans (dbu) * v.to_user<db::SimplePolygon> ();
p.assign_hull (sp.begin_hull (), sp.end_hull ());
item->add_value (p);
} else if (dbu > 0 && v.is_user<db::Edge> ()) {
item->add_value (db::CplxTrans (dbu) * v.to_user<db::Edge> ());
} else if (dbu > 0 && v.is_user<db::EdgePair> ()) {
item->add_value (db::CplxTrans (dbu) * v.to_user<db::EdgePair> ());
} else if (dbu > 0 && v.is_user<db::Path> ()) {
item->add_value (db::CplxTrans (dbu) * v.to_user<db::Path> ());
} else if (dbu > 0 && v.is_user<db::Text> ()) {
item->add_value (db::CplxTrans (dbu) * v.to_user<db::Text> ());
} else if (v.is_user<db::DBox> ()) {
item->add_value (v.to_user<db::DBox> ());
} else if (v.is_user<db::DPoint> ()) {
db::DPoint p = v.to_user<db::DPoint> ();
item->add_value (db::DEdge (p, p));
} else if (v.is_user<db::DPolygon> ()) {
item->add_value (v.to_user<db::DPolygon> ());
} else if (v.is_user<db::DSimplePolygon> ()) {
db::DPolygon p;
db::DSimplePolygon sp = v.to_user<db::DSimplePolygon> ();
p.assign_hull (sp.begin_hull (), sp.end_hull ());
item->add_value (p);
} else if (v.is_user<db::DEdge> ()) {
item->add_value (v.to_user<db::DEdge> ());
} else if (v.is_user<db::DEdgePair> ()) {
item->add_value (v.to_user<db::DEdgePair> ());
} else if (v.is_user<db::DPath> ()) {
item->add_value (v.to_user<db::DPath> ());
} else if (v.is_user<db::DText> ()) {
item->add_value (v.to_user<db::DText> ());
} else {
item->add_value (std::string (v.to_string ()));
}
}
}

View File

@ -136,6 +136,18 @@ RDB_PUBLIC_TEMPLATE void create_items_from_sequence (rdb::Database *db, rdb::id_
}
}
/**
* @brief Creates a value from a tl::Variant
*
* This produces values which reflect some values the variant can assume - specifically
* shapes are converted into corresponding RDB values. The database unit is used to
* translate integer-type values. Using a database unit of 0 will disable the conversion of
* such types.
*
* Unknown types are converted to strings.
*/
RDB_PUBLIC void add_item_value (rdb::Item *item, const tl::Variant &v, double dbu = 0.0);
}
#endif