Strange behavior probably involving redraw

edited March 2014 in Ruby Scripting

Hi,

I'm creating a script called "Smart array". The idea is that you have a PCell that you want to array, but you want each instance of the PCell to have different input parameters. You can do that manually but it's not ideal for large arrays.

So, "Smart array" takes input from a spreadsheet where each column is a PCell input (e.g. for Basic.TEXT, inputs include "text", "layer", "mag"). The input is listed in the spreadsheet. For example here is a test spreadsheet called testTEXT.xlsx:

text                      | layer                     | mag
"KLAYOUT RULES"           | RBA::LayerInfo::new(1, 0) | 2.5 
"KLAYOUT RULES AGAIN"     | RBA::LayerInfo::new(1, 0) | 2.5
"KLAYOUT RULES YET AGAIN" | RBA::LayerInfo::new(1, 0) | 2.5

And here is a supporting .rb script called "Misc tools.rb" that just does the extraction of data from the spreadsheet:

class MiscTools

  def self.get_spreadsheet_values(spreadsheet_file)

    include RBA
    require 'win32ole'

    begin
      excel = WIN32OLE.connect('excel.application')
    rescue
      excel = WIN32OLE.new('excel.application')
      RBA::MessageBox::warning("Error", "Excel is not running. Starting it in the background now.", RBA::MessageBox::Ok)
    end

    excel.DisplayAlerts = false

    wb = excel.Workbooks.Open(spreadsheet_file)

    values_read = ''

    #Loop through sheets. This is ok even if you only have data on one sheet
    wb.sheets.each do |sh|
      values_read = sh.range( sh.cells( 1, 1 ), sh.cells.specialcells( 11 )).value
    end

    wb.Close(0)
    excel.Quit unless excel.visible

    values_read    

  end

end

Lastly, here is the actual code. This code works just fine as-is, though:

  1. Start with an open layout with an open cell with layer 1 present

  2. you have to change the "load File.expand_path("./Misc/Misc tools.rb", $PHTOOLS)" line, to point to Misc tools.rb script above

  3. It only arrays in y right now (ie it makes an array with one column and in this case three rows), and ignores the x value. But if you use the default numbers and the spreadsheet above, this is an unimportant point for now.

  4. There is some strange behavior at the end, unrelated to my code (I think). Truly I don't mean for someone to have to dig through this long code, but just want to see if this strange behavior is something you've seen before. Basically what happens is the code generates the PCells as expected, but you can't see them until you:

4A. either show or hide a layer (any layer - it doesn't have to be the one the PCells are on), and then ...

4B. ...zoom in or out by some amount.

You have to do both 4A and 4B, then the PCells magically appear (though you might have to press "*" to see inside them).

Basically I think there is a redraw problem. Secondly, the trick with showing or hiding the layers as I just described is slow - sometimes you have to wait a few seconds to show or hide a layer even if there is nothing else in your layout, and the code is complete (evidenced by the "p 'Done!'" line at the end). So I don't think it's due to my code since the code running has completed.

Any ideas on how to force this redraw? Or speed the laggy-ness up?

Here is the code, which I call "Smart array.rb"

module SmartArray2

  include RBA

  lv = RBA::Application.instance.main_window.current_view
  if lv == nil
    raise "No view selected"
  end

  app = RBA::Application.instance
  mw = app.main_window
  layout = lv.active_cellview.layout
  top = lv.active_cellview.cell

  lv.transaction("Smart array")
  begin

    # Get the params from the user

    dialog = QDialog.new(Application.instance.main_window)
    dialog.windowTitle = "Smart array inputs"

    qt_layout = QVBoxLayout::new(dialog)
    dialog.setLayout(qt_layout)

    # variable text input :
    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "Source file should be an .xlsx file, with header row showing the names of the PCell values to set, obtained from '$PHTOOLS\\Misc\\Find pcell params.rb'."

    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "Spreadsheet filepath:"
    filepath = QLineEdit.new(dialog)
    qt_layout.addWidget(filepath)
    filepath.text = 'C:\filepath\\'

    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "\nSpreadsheet filename:"
    filename = QLineEdit.new(dialog)
    qt_layout.addWidget(filename)
    filename.text = 'testTEXT.xlsx'

    # x and y, and pitches, of array
    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "Number in x and y, and pitches in x and y. Note that num_x*num_y must equal the number of rows in the spreadsheet file"

    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "[num_x,num_y] = "
    num_xy = QLineEdit.new(dialog)
    qt_layout.addWidget(num_xy)
    num_xy.text = "[1,3]"

    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "[pitch_x,pitch_y] (um) = "
    pitch_xy = QLineEdit.new(dialog)
    qt_layout.addWidget(pitch_xy)
    pitch_xy.text = "[10.0,10.0]"

    # Get the list of libs and pcells -- TODO: make this work
    #libs = []
    #pcells = []
    #counter = 0;
    #RBA::Library.library_names.each_with_index { |l,i| 
    #  libs.push(l)
    #  pcell_list = #Get a list of the pcells in library l #lib.layout.pcell_declaration("TEXT")
    #  pcell_list.each_with_index { |p,j| pcells[counter] = libs[i] + "." + pcell_list[j]; counter += 1 } 
    #}

    pcells = ["Basic.TEXT","MyLib.PCellDrawSquare"] #HACK

    # combo box to choose PCell
    combo = QComboBox.new(dialog)
    pcells.each { |p|
      combo.addItem(p)
    }
    qt_layout.addWidget(combo)

    # separation line
    label = QLabel.new(dialog)
    qt_layout.addWidget(label)
    label.text = "\n"

    # OK button
    buttonOK = QPushButton.new(dialog)
    qt_layout.addWidget(buttonOK)
    buttonOK.text = " OK "
    buttonOK.clicked do 
      dialog.accept()
    end

    dialog.exec

    # Crunch the inputs above
    spreadsheet_file = filepath.text + filename.text

    chosen_lib = ""
    chosen_pcell = ""
    num_pcell_params = -1
    if combo.currentText() == "Basic.TEXT" #HACK
      chosen_lib = "Basic"
      chosen_pcell = "TEXT"
      num_pcell_params = 5 
    elsif combo.current_text() == "MyLib.PCellDrawSquare" #HACK
      chosen_lib = "MyLib"
      chosen_pcell = "PCellDrawSquare"
      num_pcell_params = 2 #HACK. Not sure if 2 is even right
    else
      raise "unknown combo box selection"
    end

    n = eval(num_xy.text) # TODO: Should probably include error checking here
    num_x = n[0] # Number of rows
    num_y = n[1] # Number of columns 
    pxy = eval(pitch_xy.text) # TODO: Should probably include error checking here
    pitch_x = pxy[0] # column pitch, in microns
    pitch_y = pxy[1] # row pitch, in microns

    # Figure out how many inputs we should have
    lib = RBA::Library.library_by_name(chosen_lib)
    pcell_decl = lib.layout.pcell_declaration(chosen_pcell)

    # Get the params from the spreadsheet
    load File.expand_path("./Misc/Misc tools.rb", $PHTOOLS)
    vals = MiscTools.get_spreadsheet_values(spreadsheet_file)

    header = vals[0][0..vals[0].length] # first (actually zeroth) row
    vals = vals[1,vals.length-1]# remove header row

    if (num_x * num_y) != vals.length
      raise "num_x * num_y is not equal to the number of rows in the spreadsheet. Click 'Cancel' on this dialog box."
    end

    ############## Start the actual code ###############
    #lv = RBA::Application.instance.main_window.current_view

    # find the lib
    lib = RBA::Library.library_by_name(chosen_lib)
    lib || raise("Unknown lib " + chosen_lib)

    # find the pcell
    pcell_decl = lib.layout.pcell_declaration(chosen_pcell)
    pcell_decl || raise("Unknown PCell " + chosen_pcell)

    # Build the string which we will "eval" later to populate each pcell with its parameters  
    param_str = []
    num_params = header.length
    param_counter = 0
    vals.each_with_index { |v,i| # Loop thru each row of data from spreadsheet. For example, v might equal ["KLAYOUT RULES", "RBA::LayerInfo::new(1, 0)", 2.5]
      param_str[param_counter] = "param = { "
      header.each_with_index { |h,j|
        v[j] = v[j].to_s
        param_str[param_counter] = param_str[param_counter] + "\"" + h + "\" => " + v[j] + ", "
      }
      temp = param_str[param_counter]
      param_str[param_counter] = temp[0..-3] # Remove the trailing ", "
      param_str[param_counter] = param_str[param_counter] + " }"
      p "Number #{param_counter} is #{param_str[param_counter]}" # Sanity check
      param_counter += 1
    }

    # Get parameters from vals
    the_hash = []
    param_arr = []
    param_str.each_with_index { |p,i|
      the_hash[i] = "#{p}"
      param_arr.push(eval(the_hash[i]))
    }
    #p param_arr[0]["text"]

    # build a param array using the param hash as a source
    pv = []
    param_arr.each { |par|
      pv.push pcell_decl.get_parameters.collect { |p|
        par[p.name] || p.default
        #p par[p.name]
      }
    }
    #p pv.length

    # create a PCell variant cell
    pcell_var = []
    pv.each { |p|
      pcell_var.push layout.add_pcell_variant(lib, pcell_decl.id, p)
    }
    #p pcell_var[0].class

    # instantiate that cell, with offsets
    t = []
    pcell_inst = []
    pcell_var.each_with_index { |pcv,i|
      #p pcv
      t.push RBA::Trans::new(0, i * pitch_y / layout.dbu)
      pcell_inst.push top.insert(RBA::CellInstArray::new(pcv, t[i]))
    }
    p 'Done!'

  end

end

Comments

  • edited March 2014

    Note also that I tried calling the MainWindow's redraw method at the end, but no change -- though, that may or may not be the problem.

    Note also that the PCells don't show up in the cell tree on the left, which is strange because I can see the text cells on the layout.

    Note lastly that occasionally I get the cryptic error message "Internal error: layEditable.cc.66 ! manager ()->transacting () was not true" which appears to be related to the issue of it being slow, after the code is complete. I haven't been able to figure out when this happens to occur though - it strangely just shows up sometimes after clicking around.

    Perhaps these symptoms are somehow related..?

  • edited March 2014

    Hi David,

    thank you for that impressive example and sorry for the late answer.

    The problem is simply that the transaction is not finished by "commit":

    ...
        p 'Done!'
    
      ensure
        lv.commit
      end
    
    end
    

    When a transaction is pending, redrawing is disabled because the layout object might still be undergoing changes. Plus some operations are disallowed in this state too (that is the meaning of the internal error).

    The ensure block will always be executed which makes sure the transaction is committed even in case of an error.

    Matthias

  • edited November -1

    That's it, simple, thanks.

    I'll post the complete code once I have bugs out and error checking etc.

    Thanks,
    David

Sign In or Register to comment.