One of the main features I use in a layout editor/viewer is to search for one (or more) text labels because we use these labels to identify terminals in hierarchical layouts. As far as I know, there is no such function in KLayout yet, please correct me if I'm wrong on that.
Before I heard of KLayout, I was using Glade and still use it if I have to search. The Glade search allows me to search texts or instance names, zoom to the found items and optionally highlight them. It also allows to search using regular expressions or a simpler wildcard search (using * for any character string or ? for any single character). It searches top-level texts only, which is what I need 99% of the time.
Is such a feature planned for KLayout?
Comments
Hallo,
such a feature is on my TODO stack, but among many other features of similar nature, so I can't give a time frame.
Basically, it's possible to add such a feature by scripting. There are a couple of examples in this forum and on the "Useful scripts" page, which can be used as a template.
Let me make one general remark here: KLayout, unlike Glade or the commercial alternatives, is open source under GPL. That means, everybody with some programming skills can add features, start his own branch or contribute the code back to me. So for open source projects, a lacking feature is not a gap, it's an opportunity.
Matthias
Simply paste the text following the dashes into a find_text.lym file and place it into the macros folder, restart KLayout and press Shift+S to open the dialog.
----
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description>Find Text</description>
<version/>
<prolog/>
<epilog/>
<doc/>
<format>general</format>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut>Shift+S</shortcut>
<show-in-menu>true</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<text>module FindTextMacro
def FindTextMacro::match(test, all)
matches = []
starts = []
mids = []
others = []
all.each do |k, v|
if k == test
matches << [k,v]
elsif k.start_with?(test)
starts << [k,v]
elsif k.include?(test)
mids << [k,v]
else
others << [k,v]
end
end
if matches.length > 0 or starts.length > 0
level = 2
elsif mids.length > 0
level = 1
else
level = 0
end
return level, matches.sort + starts.sort + mids.sort + others.sort
end
include RBA
require 'ostruct'
def FindTextMacro::find_texts(lo, cl, recursion_limit=0, prefixes=[])
texts = {}
lo.layer_indices.each do |layer_idx|
#puts "Layer index: #{layer_idx}"
cl.each_shape(layer_idx) do |shape|
if shape.is_text?
text = shape.text
point = text.trans.trans(Point.new(0,0))
name = (prefixes + [text.string]).join('.')
#texts[name] = point
texts[name] = OpenStruct.new(:point=>point, :layer_index=>layer_idx)
end
end
end
if recursion_limit > 0
known_cells = []
cl.each_inst do |inst|
child_cell = lo.cell(inst.cell_index)
cell_name = child_cell.basic_name
if not known_cells.include?(cell_name)
puts "Descending into cell #{cell_name}"
child_texts = find_texts(lo, child_cell, recursion_limit - 1, prefixes + [cell_name])
puts "Finished cell #{cell_name}"
#texts.merge!(child_texts)
child_texts.each do |name, point|
#texts[name] = inst.trans.trans(child_texts[name])
texts[name] = OpenStruct.new(:point=>inst.trans.trans(child_texts[name].point), :layer_index=>child_texts[name].layer_index)
end
known_cells << cell_name
end
end
end
return texts
end
app = Application.instance
mw = app.main_window
lv = mw.current_view
if lv == nil
raise "Find Text: No view selected"
end
cv = lv.active_cellview
lo = cv.layout
cl = cv.cell
texts = find_texts(lo, cl, 1)
dialog = QDialog::new(Application.instance.main_window)
layout = QVBoxLayout::new(dialog)
dialog.setLayout(layout)
edit = QLineEdit.new(dialog)
list = QListWidget.new(dialog)
layout.addWidget(edit)
layout.addWidget(list)
depth_label = QLabel.new("Max. hierarchy depth: ", dialog)
depth_edit = QLineEdit.new(dialog)
depth_edit.alignment = 2 # Qt::AlignRight
depth_edit.text = "1"
depth_layout = QFormLayout::new(dialog)
depth_layout.addRow(depth_label, depth_edit)
layout.addLayout(depth_layout)
def FindTextMacro::refresh_list(list, edit, texts, dialog)
dialog.windowTitle = "Find Text (#{texts.length} labels found)"
list.clear
level, matches = match(edit.text, texts)
if level == 0
edit.setStyleSheet("background-color: #FAA");
elsif level == 1
edit.setStyleSheet("background-color: #FF8");
else
edit.setStyleSheet("background-color: #AFA");
end
matches.each do |text, data|
#list.addItem "#{text} (#{data})"
#layer_name = lo.get_info(data.layer_index).name
list.addItem "#{text} (#{data.point}) #{data.layer_index}"
end
end
depth_edit.textEdited do
#puts depth_edit.text
texts = find_texts(lo, cl, depth_edit.text.to_i)
refresh_list(list, edit, texts, dialog)
end
edit.textEdited do
#puts edit.text
refresh_list(list, edit, texts, dialog)
end
def FindTextMacro::pan_to_list_selection(list, lo, lv)
if list.selectedItems().length > 0
puts list.selectedItems()[0].text
match = /\((?<x>[\d-]+),(?<y>[\d-]+)\)/.match(list.selectedItems()[0].text)
pan_pos = DPoint.new(match[:x].to_f * lo.dbu, match[:y].to_f * lo.dbu)
lv.pan_center(pan_pos)
end
end
list.itemSelectionChanged do
pan_to_list_selection(list, lo, lv)
end
list.itemClicked do
pan_to_list_selection(list, lo, lv)
end
texts.sort.each do |text, data|
list.addItem "#{text} (#{data.point}) #{data.layer_index}"
end
dialog.windowTitle = "Find Text (#{texts.length} labels found)"
dialog.show
end</text>
</klayout-macro>
Hi friendx,
that's not too bad :-)
Regarding the performance, it may help a lot to use the RecursiveShapeIterator object which is delivered by Layout#begin_shapes.
The loop then may look like this
Best regards,
Matthias