Introduction

This chapter is about programming extensions for KLayout using the integrated Ruby API (RBA) or Python API (pya).

To use RBA scripts, KLayout must be compiled with the Ruby interpreter. On Linux, this is automatically the case when the build script is run with a Ruby interpreter in the path and when the Ruby development packages are installed (on Debian these packages are "ruby-1.9.1" and "ruby1.9.1-dev" for example). A Ruby version more recent than 1.9 is recommended. Ruby 2.0 and later versions are supported too.

To use pya scripts, KLayout must be compiled with the Python interpreter. On Linux, this is automatically the case when the build script is run with a Python interpreter in the path and when the Python development packages are installed. A Python version more recent than 2.7 is recommended. Python 3.0 and later versions are supported too.

Basically there are a traditional, script-based style of programming and the macro-based style supported by version 0.22. Scripts are simple text files which are prepared externally and KLayout acts as an interpreter for these scripts. That traditional style was the only way to run scripts in version 0.21 and earlier. It is still supported and described in Traditional Ruby Programming. From version 0.22, generic macros are supported which offer some advantages over plain text files.

Macros are special XML files which contain Ruby code plus some additional information required to link them into the system, i.e. automatically execute them on startup or provide menu entries for them. They can load normal ".rb" files to implement libraries of classes in the usual way. Macros are managed and developed conveniently in the integrated macro development environment along with the supporting files. This method is the preferred way of creating application extensions and is described in this chapter.

Before you start, please make yourself familiar with the macro development integrated environment (About Macro Development). This documentation also assumes that you familiar with the Ruby programming language. There are numerous books and tutorials about Ruby. The most famous one is the "pickaxe book" (Programming Ruby - The Pragmatic Programmers Guide) by Dave Thomas. If you are familiar with Ruby there is a technical article about the way Ruby and KLayout's core are integrated (The Ruby Language Binding). There are special articles about the integrated Qt binding (The Qt Binding) and PCell programming (Coding PCell's In Ruby). If you want to use Python, please read the python implementation article (Using Python) for details about how to translate Ruby samples into Python and specific details of the Python integration.

An introduction into the basic concepts of the KLayout API are given in the article about the application API (The Application API) and about the database API (The Database API).

A First Sample

The first sample is already a complete macro which counts all selected paths, boxes, polygons or text objects. It demonstrates how to set up a macro, how to deal with the selection and how to access the layout database.

Here is the code:

module MyMacro

  include RBA

  app = Application.instance
  mw = app.main_window

  lv = mw.current_view
  if lv == nil
    raise "Shape Statistics: No view selected"
  end

  paths = 0
  polygons = 0
  boxes = 0
  texts = 0

  lv.each_object_selected do |sel|

    shape = sel.shape

    if shape.is_path?
      paths += 1
    elsif shape.is_box?
      boxes += 1
    elsif shape.is_polygon?
      polygons += 1
    elsif shape.is_text?
      texts += 1
    end

  end

  s = "Paths: #{paths}\n"
  s += "Polygons: #{polygons}\n"
  s += "Boxes: #{boxes}\n"
  s += "Texts: #{texts}\n"

  MessageBox::info("Shape Statistics", s, MessageBox::Ok)

end

To run the macro, create a new macro in the macro development IDE: choose "Macros/Macro Development". Create a new macro using the "+" button. Rename the macro to a suitable name. Copy the code above into the text. Load a layout, select some objects and in the macro development IDE press F5. A message box will appear the tells us how may boxes, polygons etc. we have selected.

If we look at the code, the first observation is that we put the whole script into our own namespace (we can use any name which is not used otherwise). The advantage of that approach is that we can import the "RBA" namespace which makes life somewhat easier. The RBA namespace contains all KLayout classes and constants. If we would import the RBA namespace into the main namespace we would do that for all other scripts in KLayout since the main namespace is a common resource for all scripts.

The first thing we do inside the macro is to access the layout view:

  app = Application.instance
  mw = app.main_window

  lv = mw.current_view
  if lv == nil
    raise "Shape Statistics: No view selected"
  end

The Application class (Application) is a representative for the KLayout application. Since there is only one application, it is a singleton. We can obtain the singleton instance with the class method ("static" in the language of C++) "instance". It delivers a reference to the only Application object which is the main entrance to all internals of KLayout.

The next object which is important is the MainWindow object (MainWindow). Currently there is only one MainWindow object which can be obtained with the "main_window" method of the Application object. The MainWindow object represents the application's window and manages the top level visual objects of the application. The main visual components of the main window are the menus, the tool panels (cell tree, layer list, tool box, navigator ...) and the layout views.

The layout view is the representation of a layout tab (LayoutView). That is basically the window to the layouts loaded into that tab. All related information such as the display settings, the zoom area, the layer properties and the informations about the cell shown, the hierarchy levels and further settings go here.

A main window can display multiple tabs. Hence there are multiple LayoutView objects available. The currently selected tab can be addressed with the "current_view" method. This method delivers the LayoutView object associated with that tab. If no layout is loaded, that method returns nil (the Ruby for "nothing")

The actual layouts loaded are separate entities. Technically, there is a many-to-many relationship between layout views and layout objects. A layout view may display multiple layouts are one layout may be displayed in multiple layout views. In addition, a layout view can address different cells from a layout. A layout view has a current cell and a path that leads to that cell. The path consists of a specific and unspecific part. The unspecific part of the path tells where the cell we show as the current cell is located in the cell tree. The unspecific part is a tribute to the fact that a cell can appear in different branches of the cell tree (i.e. as child of cell A and cell B). The specific part of the path addresses a specific instance of within some cell (the "context cell") above in the hierarchy. A specific path is created when we descend down in the hierarchy along a certain instantiation path.

Layout, current cell, context cell, specific and unspecific path are combined into the CellView object (CellView). A layout view can have multiple cell views corresponding to the different layouts that can be loaded into a panel. The cell views do not necessarily have to point to different layouts.

For our sample we don't need the CellView objects, because we get all information directly from the view. But the concept of that object is important to understand the API documentation.

In our sample, we ask the layout view for all selected objects and collect the object counts:

  lv.each_object_selected do |sel|

    shape = sel.shape

    if shape.is_path?
      paths += 1
    elsif shape.is_box?
      boxes += 1
    elsif shape.is_polygon?
      polygons += 1
    elsif shape.is_text?
      texts += 1
    end

  end

"each_object_selected" is a method of the LayoutView object. More precisely it's an iterator which calls the given block for each selected object. Since the layout view can show multiple layouts, the selected objects may originate from different layouts. In addition, the object may be selected in a child cell of the current cell. Hence, the selection is described by a cell view index, an instantiation path (a sequence of instances leading to the cell containing the selected object from the current cell) and the actual object selected. That information is combined into an ObjectInstPath object (ObjectInstPath).

In our case we are not interested in the cell view that shape lives in. Neither are we in the instantiation path. Hence all we need is the shape and we can obtain it with the "shape" method. This method delivers a Shape object (Shape), which is some kind of pointer to the actual shape. The actual shape is either a polygon, a box, a text or a path. The Shape object has a multiple identity and we can ask it what shape type is represents. For that, the Shape object offers methods like "is_box?" etc. If we know the type we can ask it for the actual object and fetch a Polygon (Polygon), a Box (Box), a Text (Text) or a Path object (Path). For our sample however we don't need access to the actual object.

Finally we put together a message and display it in a message box:

  s = "Paths: #{paths}\n"
  s += "Polygons: #{polygons}\n"
  s += "Boxes: #{boxes}\n"
  s += "Texts: #{texts}\n"

  MessageBox::info("Shape Statistics", s, MessageBox::Ok)

MessageBox (MessageBox) is a class that provides modal message dialogs through several class methods. "info" shows an information box and with the given title and message. The third parameter indicates which buttons will be shown. In that case, one "Ok" button is sufficient because we don't want to take specific actions when the message box is closed.

This is just a simple example, but it already illustrates some basic concepts. For a in-depth introduction into the API, read The Application API and The Database API.