Text search function?

edited September 2012 in KLayout Development
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

  • edited November -1

    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

  • edited November -1
    All right, here is my first Ruby script and KLayout extension! It's not perfect as it finds (mainly for performance reasons) only one label with the same string (think of VDD labels in standard cell regions), but it's a start.

    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>
  • edited December 2012

    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

     iter = lo.begin_shapes(cl.cell_index, layer_idx)
     while !iter.at_end?
       if iter.shape.is_text?
         text = iter.shape.text
         # ... handle the text.
         # use iter.trans to transform the text
       end
       iter.next 
     end
    

    Best regards,

    Matthias

Sign In or Register to comment.